@Transactional

@Transactional注解及其支持类所提供的功能最低要求使用Java 5(Tiger)。 html

除了基于XML文件的声明式事务配置外,你也能够采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的作法让事务声明和将受其影响的代码距离更近了,并且通常来讲不会有不恰当的耦合的风险,由于,使用事务性的代码几乎老是被部署在事务环境中。 spring

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

<!-- the service class that we want to make 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);
}

当上述的POJO定义在Spring IoC容器里时,上述bean实例仅仅经过 行xml配置就可使它具备事务性的。以下: apache

<!-- 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.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.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"/> <!-- aPlatformTransactionManageris 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>

提示

实际上,若是你用'transactionManager'来定义PlatformTransactionManagerbean的名字的话,你就能够忽略<tx:annotation-driven/>标签里的'transaction-manager'属性。 若是PlatformTransactionManagerbean你要经过其它名称来注入的话,你必须用'transaction-manager'属性来指定它,如上所示。 编程

方法的可见度和@Transactional oracle

@Transactional注解应该只被应用到 public 可见度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用@Transactional注解,它也不会报错, 可是这个被注解的方法将不会展现已配置的事务设置。 app

@Transactional注解能够被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅@Transactional注解的出现不足于开启事务行为,它仅仅 是一种元数据,可以被能够识别@Transactional注解和上述的配置适当的具备事务行为的beans所使用。上面的例子中,其实正是<tx:annotation-driven/>元素的出现 开启 了事务行为。 框架

Spring团队的建议是你在具体的类(或类的方法)上使用@Transactional注解,而不要使用在类所要实现的任何接口上。你固然能够在接口上使用@Transactional注解,可是这将只能当你设置了基于接口的代理时它才生效。由于注解是 不能继承 的,这就意味着若是你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,并且对象也将不会被事务代理所包装(将被确认为严重的)。所以,请接受Spring团队的建议而且在具体的类上使用@Transactional注解。 eclipse

注意

当使用@Transactional风格的进行声明式事务定义时,你能够经过<tx:annotation-driven/>元素的 "proxy-target-class" 属性值来控制是基于接口的仍是基于类的代理被建立。若是 "proxy-target-class" 属值被设置为 "true",那么基于类的代理将起做用(这时须要CGLIB库cglib.jar在CLASSPATH中)。若是 "proxy-target-class" 属值被设置为 "false" 或者这个属性被省略,那么标准的JDK基于接口的代理将起做用。 ide

在多数情形下,方法的事务设置将被优先执行。在下列状况下,例如: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  }
}

9.5.6.1. @Transactional有关的设置

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

  • 事务传播设置是PROPAGATION_REQUIRED

  • 事务隔离级别是ISOLATION_DEFAULT

  • 事务是 读/写

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

  • 任何RuntimeException将触发事务回滚,可是任何 checkedException将不触发事务回滚

这些默认的设置固然也是能够被改变的。@Transactional注解的各类属性设置总结以下:

表 9.2. @Transactional注解的属性

属性 类型 描述
传播性 枚举型:Propagation 可选的传播性设置
隔离性 枚举型:Isolation 可选的隔离性级别(默认值:ISOLATION_DEFAULT)
只读性 布尔型 读写型事务 vs. 只读型事务
超时 int型(以秒为单位) 事务超时
回滚异常类(rollbackFor) 一组Class类的实例,必须是Throwable的子类 一组异常类,遇到时 必须 进行回滚。默认状况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。
回滚异常类名(rollbackForClassname) 一组Class类的名字,必须是Throwable的子类 一组异常类名,遇到时 必须 进行回滚
不回滚异常类(noRollbackFor) 一组Class类的实例,必须是Throwable的子类 一组异常类,遇到时 必须不 回滚。
不回滚异常类名(noRollbackForClassname) 一组Class类的名字,必须是Throwable的子类 一组异常类,遇到时 必须不 回滚


9.5.7. 插入事务操做

考虑这样的状况,你有一个类的实例,并且但愿 同时插入事务性通知(advice)和一些简单的剖析(profiling)通知。那么,在<tx:annotation-driven/>环境中该怎么作?

咱们调用updateFoo(Foo)方法时但愿这样:

  • 配置的剖析切面(profiling aspect)开始启动,

  • 而后进入事务通知(根据配置建立一个新事务或加入一个已经存在的事务),

  • 而后执行原始对象的方法,

  • 而后事务提交(咱们假定这里一切正常),

  • 最后剖析切面报告整个事务方法执行过程花了多少时间。

注意

这章不是专门讲述AOP的任何细节(除了应用于事务方面的以外)。请参考 第 6 章 使用Spring进行面向切面编程(AOP) 章以得到对各类AOP配置及其通常概念的详细叙述。

这里有一份简单的剖析切面(profiling aspect)的代码。(注意,通知的顺序是由Ordered接口来控制的。要想了解更多细节,请参考 第 6.2.4.7 节 “通知(Advice)顺序” 节。)

package x.y;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;
public class SimpleProfiler implements Ordered {
private int order; // allows us to control the ordering of advice public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
} // this method is the around advice public Object profile(ProceedingJoinPoint call) throws Throwable {
Object returnValue;
StopWatch clock = new StopWatch(getClass().getName());
try {
clock.start(call.toShortString());
returnValue = call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
return returnValue;
}
}

这里是帮助知足咱们上述要求的配置数据。

<?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.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="fooService" class="x.y.service.DefaultFooService"/> <!-- this is the aspect --> <bean id="profiler" class="x.y.SimpleProfiler"> <!-- execute before the transactional advice (hence the lower order number) -->  <property name="order" value="1"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<aop:config> <!-- this advice will execute around the transactional advice --> <aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>
</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>
</beans>

上面配置的结果将得到到一个拥有剖析和事务方面的 按那样的顺序 应用于它上面的'fooService'bean。 许多附加的方面的配置将一块儿达到这样的效果。

最后,下面的一些示例演示了使用纯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.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="fooService" class="x.y.service.DefaultFooService"/> <!-- the profiling advice --> <bean id="profiler" class="x.y.SimpleProfiler"> <!-- execute before the transactional advice (hence the lower order number) --> <property name="order" value="1"/>
</bean>
<aop:config>
<aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/> <!-- will execute after the profiling advice (c.f. the order attribute) -->  <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod"order="2"/> <!-- order value is higher than the profiling aspect --> <aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice> <!-- other <bean/> definitions such as aDataSourceand aPlatformTransactionManagerhere --> </beans>

上面配置的结果是建立了一个'fooService'bean,剖析方面和事务方面被 依照顺序 施加其上。若是咱们但愿剖析通知在目标方法执行以前 后于 事务通知执行,并且在目标方法执行以后 先于 事务通知,咱们能够简单地交换两个通知bean的order值。

若是配置中包含更多的方面,它们将以一样的方式受到影响。

9.5.8. 结合AspectJ使用@Transactional

经过AspectJ切面,你也能够在Spring容器以外使用Spring框架的@Transactional功能。要使用这项功能你必须先给相应的类和方法加上@Transactional注解,而后把spring-aspects.jar文件中定义的org.springframework.transaction.aspectj.AnnotationTransactionAspect切面链接进(织入)你的应用。一样,该切面必须配置一个事务管理器。你固然能够经过Spring框架容器来处理注入,但由于咱们这里关注于在Spring容器以外运行应用,咱们将向你展现如何经过手动书写代码来完成。

注意

在咱们继续以前,你可能须要好好读一下前面的第 9.5.6 节 “使用@Transactional”第 6 章 使用Spring进行面向切面编程(AOP) 两章。

// construct an appropriate transaction manager  DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource()); // configure theAnnotationTransactionAspectto use it; this must be done before executing any transactional methods AnnotationTransactionAspect.aspectOf().setTransactionManager (txManager);

注意

使用此切面(aspect),你必须在 实现 类(和/或类里的方法)、而 不是 类的任何所实现的接口上面进行注解。AspectJ遵循Java的接口上的注解 不被继承 的规则。

类上的@Transactional注解指定了类里的任何 public 方法执行的默认事务语义。

类里的方法的@Transactional将覆盖掉类注解的默认事务语义(如何存在的话)。public、protected和默承认见的方法可能都被注解。直接对protected和默承认见的方法进行注解,让这些方法在执行时去获取所定义的事务划分是惟一的途径。

要把AnnotationTransactionAspect织入你的应用,你或者基于AspectJ构建你的应用(参考 AspectJ Development Guide),或者采起“载入时织入”(load-time weaving),参考 第 6.8.4 节 “在Spring应用中使用AspectJ Load-time weaving(LTW)” 得到关于使用AspectJ进行“载入时织入”的讨论。

相关文章
相关标签/搜索