Spring事务管理(annotation)

来源:互联网 发布:穷人的生活 知乎 编辑:程序博客网 时间:2024/06/05 05:47

5.6 使用@Transactional

除了使用XML类型的事务管理,同时Spring也提供了Annotation类型的事务管理。如下所示:

// the service class that we want to make transactional@Transactionalpublic class DefaultFooService implements FooService {  Foo getFoo(String fooName);  Foo getFoo(String fooName, String barName);  void insertFoo(Foo foo);  void updateFoo(Foo foo);}

当然,需要在Spring的配置文档中添加一行,用于说明事务是由哪个txManager管`理的。

<!-- 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-3.0.xsd     http://www.springframework.org/schema/tx     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd     http://www.springframework.org/schema/aop     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  <!-- this is the service object that we want to make transactional -->  <bean id="fooService" class="x.y.service.DefaultFooService"/>  <!-- enable the configuration of transactional behavior based on annotations -->  <tx:annotation-driven transaction-manager="txManager"/>  <!-- a PlatformTransactionManager is still required -->  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- (this dependency is defined somewhere else) -->  <property name="dataSource" ref="dataSource"/>  </bean>  <!-- other <bean/> definitions here --></beans>

可以使用@Transactional标记一个接口,接口中的方法,一个类的定义或者类中的公共方法。但是仅仅提供@Transactional标记是不够的,@Transactional标记只是一种能够被某种运行时架构使用的配置元数据,因此需要提供能够自动识别@Transactional标记的架构支持。上例中,使用<tx:annotation-driven/>元素开启了标记的事务行为。

注意:当事务管理使用的是代理形式,仅有在公有方法上标记的@Transactional是有效的,所有的私有的、受保护的或者包可见性的方法即使标记了@Transactional也不会有实质性的事务管理行为产生,并且系统不会给出任何错误或者提示信息。如果有必要在非公有方法上标记事务,那么不应当使用代理模式的事务管理,可以考虑使用AspectJ。
注意:Spring提倡将@Transactional标记在类(或者类的方法上),不提倡对接口(或者接口方法进行标记)。在接口或者接口方法上进行@Transactional标记是可行的,但是仅有系统运行在基于接口的代理前提下事务管理才会发生。实际上Java标记不会通过接口继承,这意味着如果你使用基于类的代理(prox-target-calss=”ture”)或者使用weaving-based aspact(mode=”aspectj”),这样在接口上的标记将不会被代理或者织入架构识别,并且对象不会被事务代理包装,最终无法实现事务管理。
注意:在代理模式中(是Spring事务管理默认使用的),仅有外部方法调用过程才会被代理截获,这意味着自身调用,即一个方法调用了本对象的另外一个方法不会导致一个实质的事务管理代理过程产生,即使是被调用的方法标记了@Transactional

AspectJ模式与代理模式不同,能够使应用自身调用依然被事务管理包围。AspectJ不使用代理,而是在字节码基础上将事务处理逻辑添加到对象中。(这样代码量明显增加)。

<tx:annotation-driven/>的配置属性描述如下:

(1)transaction-manager,默认transactionManager,事务管理器的名字,仅当事务管理器不是transactionManager时,需要显示指定。
(2)mode,默认proxy,proxy模式仅当外部调用产生时才产生代理。可替代的选项是aspectj,通过更改类的字节码来提供事务功能的织入。aspectJ织入需要提供spring-aspects.jar包支持。
(3)proxy-target-class,默认为false。只适用于proxy方式,用于控制代理创建的方式。当值为true时,基于class类型的代理将被创建,否则,基于标准的JDK接口的代理将被创建。
(4)order,默认为Orderd.LOWEST_PRECEDENCE,定义多个advice执行的次序,默认情况下由系统决定。

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  }}

5.6.1 @Transactional设置

(1)value 类型String,可选的属性,用于指定事务管理者的名字(Transaction Manager)。
(2)propagation 类型enum:Propagation。可选的,事务传播行为。
(3)isolation 类型enum:Isolation。可选的,事务隔离级别。
(4)timeout 类型int。事务失效时间。
(5)readonly类型boolean。read/write或者read-only事务设置。
(6)rollbackeFor 类型是Throwable子类的数组(对象)。设置出发rollback异常事件。
(7)rollbackForClassname,类型是Throwable子类的数组(类名)。
(8)noRollbackFor
(9)noRollbackForClassname

5.6.2配置多个Transaction Manager

public class TransactionalService {    @Transactional("order")    public void setSomething(String name) { ... }    @Transactional("account")    public void doSomething() { ... }  }

配置文件如下:

<tx:annotation-driven/>  <bean id="transactionManager1" class="org.springframework.jdbc.DataSourceTransactionManager">    ...    <qualifier value="order"/>  </bean>  <bean id="transactionManager2" class="org.springframework.jdbc.DataSourceTransactionManager">   ...    <qualifier value="account"/>  </bean> ``##5.6.3 定义shortcut标记

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(“order”)
public @interface OrderTx {

}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(“account”)
public @interface AccountTx {
}

使用方式如下:

public class TransactionalService {
@OrderTx
public void setSomething(String name) { … }
@AccountTx
public void doSomething() { … }
}
“`

5.7 事务传播(propagation)

在理解事务传播之前,需要先理解物理(physical)事务和逻辑(logical)事务。物理事务是一个真正的事务,而逻辑事务是某段代码内部的事务处理(例如一个内嵌方法内部)。

5.7.1 Required

PROPAGATION_REQUIRED事务传播方式中,会为每一个方法创建一个逻辑事务。每一个内部逻辑事务可以独立于外部逻辑事务单独设置rollback-only的状态,但是所有的逻辑事务最终都会映射为一个物理事务。所以,内部逻辑事务产生的rollback-only状态将会直接影响到外部逻辑事务的实质提交。

当内部逻辑事务设置rollback-only时,而外部事务仍然没有决定事务是否应该回滚(因为内部设置回滚时,外部并不知道内部已经设置了),这个回滚对外部来说是不可预期的。因此需要在内部抛出一个UnexpectedRollbackException的异常,根据这个预期的行为,外部事务会获得内部事务并没有按照预期的方式执行的通知。如果一个内部的逻辑事务无声的标记了rollback-only,但是外部并有感知到内部的这一变化,那么外部调用者将会继续执行提交。因此,外部调用者需要获得一个UnexpectedRollbackException异常,以便清楚的确定内部事务已经回滚,不要在继续执行提交任务。

5.7.2 RequiredNew

PROPAGATION_REQUIRES_NEW与PROPAGATON_REQUIRED不同,对每一个逻辑事务使用一个完整的物理事务,在这种情况下,底层的物理事务是不同的,因此可以独立的提交和回滚,因此内部的事务回滚状态不会影响到外部事务。

5.7.3 Nested

PROPAGATION_NESTED使用一个物理事务管理多个检查点,事务可以回滚到某个检查点。这种部分回滚策略,允许内部事务回滚自己处理范围,即使内部事务回滚,外部事务依然可以继续执行提交。这种设置只适用于JDBC(JDBC中设置有事务管理的检查点)。

0 0
原创粉丝点击