spring事务配置的两种方式:

1.基于XML的事务配置。2.基于注解方式的事务配置。

前言:在我们详细介绍spring的两种声明式事务管理之前,我们需要先理解这些概念

1)spring的事务管理是通过Aop的方式来实现;
2)声明式事务是spring对事务管理的最常用的方式,因为这种方式对代码的影响最小,因此也就符合非侵入式的轻量级的容器的概念;
3)我们需要理解事务的概念,这里不再给出详细说明。

正文:

1.基于XMl的事务配置

现在假设我们有这样一个接口:
package x.y.service;    
public interface FooService {    
  Foo getFoo(String fooName);    
  Foo getFoo(String fooName, String barName);    
  void insertFoo(Foo foo);    
  void updateFoo(Foo foo);    
}   

 

</pre>对应于这个接口有一个实现类如下:<pre name="code" class="java">package x.y.service;    
public class DefaultFooService implements FooService {    
  public Foo getFoo(String fooName) {    
    throw new UnsupportedOperationException();    
  }    
  public Foo getFoo(String fooName, String barName) {    
    throw new UnsupportedOperationException();    
  }    
  public void insertFoo(Foo foo) {    
    throw new UnsupportedOperationException();    
  }    
  public void updateFoo(Foo foo) {    
    throw new UnsupportedOperationException();    
  }}    

 


注:上述两端代码是对应于业务层的。我们平时配置事务也最好在业务层进行配置。
 
接下来我们看一下spring 的配置文件中需要我们配置什么吧
<!-- from the file 'context.xml' -->    
<?xml version="1.0" encoding="UTF-8"?>    
<beans xmlns="http://www.springframework.org/schema/beans"    
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
     xmlns:aop="http://www.springframework.org/schema/aop"    
     xmlns:tx="http://www.springframework.org/schema/tx"    
     xsi:schemaLocation="    
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd    
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">    
      
  <!-- 首先我们要把服务对象fooService声明成一个bean -->    
  <bean id="fooService" class="x.y.service.DefaultFooService"/>    
    
  <!-- 然后是声明一个事物建议tx:advice,spring为我们提供了事物的封装,这个就是封装在了<tx:advice/>中 -->  
  <!-- <tx:advice/>有一个transaction-manager属性,我们可以用它来指定我们的事物由谁来管理。 -->  
  <tx:advice id="txAdvice" transaction-manager="txManager">    
  <!-- 配置这个事务建议的属性 -->    
  <tx:attributes>    
    <!-- 指定所有get开头的方法执行在只读事务上下文中 -->    
    <tx:method name="get*" read-only="true"/>    
    <!-- 其余方法执行在默认的读写上下文中 -->    
    <tx:method name="*"/>    
  </tx:attributes>    
  </tx:advice>    
      
  <!-- 我们定义一个切面,它匹配FooService接口定义的所有操作 -->    
  <aop:config>    
    <!-- <aop:pointcut/>元素定义AspectJ的切面表示法,这里是表示x.y.service.FooService包下的任意方法。 -->  
  <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>    
    <!-- 然后我们用一个通知器:<aop:advisor/>把这个切面和tx:advice绑定在一起,表示当这个切面:fooServiceOperation执行时tx:advice定义的通知逻辑将被执行 -->  
  <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>    
  </aop:config>    
      
  <!-- 数据元信息 -->    
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">    
  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>    
  <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>    
  <property name="username" value="scott"/>    
  <property name="password" value="tiger"/>    
  </bean>    
    
  <!-- 管理事务的类-->    
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
  <property name="dataSource" ref="dataSource"/>    
  </bean>     
  <!-- other <bean/> definitions here -->    
</beans>

 


    

 
现在我通俗的把上面的配置讲解一下:
首先我们应该要把服务对象'fooService' 声明成一个bean,我们要把一个服务对象('fooService' bean)做成事务性的。
我们就应该首先在声明一个事务管理的建议,用什么来管理,spring给我们提供了事务封装,这个就封装在了<tx:advice/>中,
这个事务建议给我们提供了一个transaction-manager属性,用他可以指定我们用谁来管理我们的事务。我们上边的例子用的
为一个指向 PlatformTransactionManager bean的名字(这里指 'txManager'), 该bean将会真正管理事务。上面用的事务
管理类是用的jdbc中提供的事务管理,当然这里也可以指定为hibernate管理。当然了,不管用那个类来管理我们的事务,都
不要忘记了提供我们的datasource属性,因为事务管理也需要这里面的信息。我们声明好事务建议,也指定好了具体用哪个
类来管理了,下面我们的任务就是要把我们定义好的这些利用AOP把我们的事务管理织入到我们的业务逻辑里面了。
<aop:config/> 的定义, 它确保由 'txAdvice'  bean定义的事务通知在应用中合适的点被执行。 
首先我们定义了 一个切面,它匹配 FooService 接口定义的所有操作, 
我们把该切面叫做 'fooServiceOperation'。<aop:pointcut/> 元素定义是AspectJ的切面表示法,
上述表示x.y.service.FooService包下的任意方法。然后我们用一个通知器(advisor)把这个切面与 'txAdvice' 绑定在一起,
 表示当 'fooServiceOperation' 执行时,'txAdvice' 定义的通知逻辑将被执行。大体流程就是这样的了。

<tx:advice/> 有关的设置

通过 <tx:advice/> 标签来指定不同的事务性设置。默认的 <tx:advice/> 设置如下:

事务传播设置是 REQUIRED

隔离级别是DEFAULT

事务是 读/写

事务超时默认是依赖于事务系统的,或者事务超时没有被支持。

任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

这些默认的设置当然也是可以被改变的。 <tx:advice/> 和 <tx:attributes/> 标签里的 <tx:method/> 各种属性设置总结如下:

Table 9.1. <tx:method/> 有关的设置

属性

是否需要?

默认值

描述

name

 

与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:'get*'、'handle*'、'on*Event'等等。

propagation

REQUIRED

事务传播行为

isolation

DEFAULT

事务隔离级别

timeout

-1

事务超时的时间(以秒为单位)

read-only

false

事务是否只读?

rollback-for

 

将被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'

no-rollback-for

 

不 被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'

 

下面我们具体来看一下事务的传播性的几个值:

REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。


NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。


REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。


MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。


SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。


NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。


NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务 拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

到此为止基于XML的事务配置就算完成了。

 



使用 @Transactional

 

 

除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。

下面的例子很好地演示了 @Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义

 

@Transactional    
public class DefaultFooService implements FooService {    
  Foo getFoo(String fooName);    
  Foo getFoo(String fooName, String barName);    
  void insertFoo(Foo foo);    
  void updateFoo(Foo foo);    
}    

 

 

 

当我们使用注解式声明事务时,在XML中只需要一句话就ok了

 

<?xml version="1.0" encoding="UTF-8"?>    
<beans xmlns="http://www.springframework.org/schema/beans"    
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
     xmlns:aop="http://www.springframework.org/schema/aop"    
     xmlns:tx="http://www.springframework.org/schema/tx"    
     xsi:schemaLocation="    
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd    
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">    
      
  <bean id="fooService" class="x.y.service.DefaultFooService"/>    
   <tx:annotation-driven transaction-manager="txManager"/>    
   <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
   <property name="dataSource" ref="dataSource"/>    
  </bean>    
</beans>  

 



 

我们知道 @Transactional 注解可以声明在类上,也可以声明在方法上。在大多数情况下,方法上的事务会首先执行

例如: DefaultFooService 类在类的级别上被注解为只读事务,但是,这个类中的 updateFoo(Foo) 方法的 @Transactional 注解的事务设置将优先于类级别注解的事务设置。

 

@Transactional(readOnly = true)    
public class DefaultFooService implements FooService {    
  public Foo getFoo(String fooName) {    
    // do something    
  }    
    // these settings have precedence for this method    
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)    
    public void updateFoo(Foo foo) {    
        // do something    
           
    }    
}    

 

 

 

@Transactional 有关的设置

 

@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据。 如:“当一个方法开始调用时就开启一个新的只读事务,并停止掉任何现存的事务”。 默认的 @Transactional 设置如下:

事务传播设置是 PROPAGATION_REQUIRED

事务隔离级别是 ISOLATION_DEFAULT

事务是 读/写

事务超时默认是依赖于事务系统的,或者事务超时没有被支持。

任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

这些默认的设置当然也是可以被改变的。 @Transactional 注解的各种属性设置总结如下:

 @Transactional 注解的属性

属性

类型

描述

propagation

枚举型:Propagation

可选的传播性设置

isolation

枚举型:Isolation

可选的隔离性级别(默认值:ISOLATION_DEFAULT)

readOnly

布尔型

读写型事务 vs. 只读型事务

timeout

int型(以秒为单位)

事务超时

rollbackFor

一组 Class 类的实例,必须是Throwable 的子类

一组异常类,遇到时 必须 进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。

rollbackForClassname

一组 Class 类的名字,必须是Throwable的子类

一组异常类名,遇到时 必须 进行回滚

noRollbackFor

一组 Class 类的实例,必须是Throwable 的子类

一组异常类,遇到时 必须不 回滚。

noRollbackForClassname

一组 Class 类的名字,必须是Throwable 的子类

一组异常类,遇到时 必须不 回滚

     在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是全限定名+"."+事务通知的类的方法名。比如BusinessService类的handlePayment(..)方法启动了一个事务,事务的名称是:

com.foo.BusinessService.handlePayment



 

 


 

 

使用 @Transactional

 

除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。

下面的例子很好地演示了 @Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义

@Transactional 有关的设置

 

@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据。 如:“当一个方法开始调用时就开启一个新的只读事务,并停止掉任何现存的事务”。 默认的 @Transactional 设置如下:

事务传播设置是 PROPAGATION_REQUIRED

事务隔离级别是 ISOLATION_DEFAULT

事务是 读/写

事务超时默认是依赖于事务系统的,或者事务超时没有被支持。

任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

这些默认的设置当然也是可以被改变的。 @Transactional 注解的各种属性设置总结如下:

 @Transactional 注解的属性

属性

类型

描述

propagation

枚举型:Propagation

可选的传播性设置

isolation

枚举型:Isolation

可选的隔离性级别(默认值:ISOLATION_DEFAULT)

readOnly

布尔型

读写型事务 vs. 只读型事务

timeout

int型(以秒为单位)

事务超时

rollbackFor

一组 Class 类的实例,必须是Throwable 的子类

一组异常类,遇到时 必须 进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。

rollbackForClassname

一组 Class 类的名字,必须是Throwable的子类

一组异常类名,遇到时 必须 进行回滚

noRollbackFor

一组 Class 类的实例,必须是Throwable 的子类

一组异常类,遇到时 必须不 回滚。

noRollbackForClassname

一组 Class 类的名字,必须是Throwable 的子类

一组异常类,遇到时 必须不 回滚

     在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是全限定名+"."+事务通知的类的方法名。比如BusinessService类的handlePayment(..)方法启动了一个事务,事务的名称是:

com.foo.BusinessService.handlePayment