Detailed usage of spring's @Transactional annotation

Overview

Transaction management is crucial for enterprise applications, which can ensure data consistency even in abnormal situations.
Spring Framework provides a consistent abstraction for transaction management with the following characteristics:

  • Provide a consistent programming model for different transaction APIs, such as JTA (Java Transaction API), JDBC, Hibernate, JPA (Java Persistence API and JDO (Java Data Objects)
  • Support declarative transaction management, especially annotation-based declarative transaction management, easy to use
  • Provides a simpler programmatic transaction management API than other transaction APIs such as JTA
  • Perfect integration with spring data access abstraction

business management

Spring supports both programmatic transaction management and declarative transaction management.

Programmatic transaction management using TransactionTemplate or directly using the underlying PlatformTransactionManager. For programmatic transaction management, Spring recommends using TransactionTemplate.

Declarative transaction management is built on top of AOP. Its essence is to intercept the method before and after, and then create or join a transaction before the target method starts, and commit or roll back the transaction according to the execution status after the target method is executed. The biggest advantage of declarative transactions is that there is no need to programmatically manage transactions, so there is no need to mix transaction management code in business logic code, just make relevant transaction rule declarations in configuration files (or through @Transactional Annotation method), you can apply transaction rules to business logic.

Obviously declarative transaction management is better than programmatic transaction management, which is the non-intrusive development method advocated by spring. Declarative transaction management keeps business code from being polluted. An ordinary POJO object can get full transaction support as long as it is annotated. Compared with programmatic transactions, the only disadvantage of declarative transactions is that the finer granularity of the latter can only be applied to the method level, and cannot be applied to the code block level like programmatic transactions. But even if there is such a demand, there are many workarounds, for example, the code block that needs transaction management can be independent as a method and so on.

There are also two common ways of declarative transaction management, one is based on the xml configuration file of the tx and aop namespaces, and the other is based on the @Transactional annotation. Obviously, the annotation-based method is easier to use and cleaner.

Whether AutoCommit and whether to automatically commit when the connection is closed

auto commit

By default, the database is in autocommit mode. Each statement is in a separate transaction. When the statement is executed, the transaction is implicitly committed if the execution is successful, and the transaction
is implicitly rolled back if the execution fails.

For normal transaction management, a group of related operations are in a transaction, so the autocommit mode of the database must be turned off. However, we don't have to worry about this, spring will set the autocommit feature of the underlying connection to false.
org/springframework/jdbc/datasource/DataSourceTransactionManager.java

复制代码
 1 // switch to manual commit if necessary. this is very expensive in some jdbc drivers,
 2 // so we don't want to do it unnecessarily (for example if we've explicitly
 3 // configured the connection pool to set it already).
 4 if (con.getautocommit()) {
 5     txobject.setmustrestoreautocommit(true);
 6     if (logger.isdebugenabled()) {
 7         logger.debug("switching jdbc connection [" + con + "] to manual commit");
 8     }
 9     con.setautocommit(false);
10 }
复制代码

有些数据连接池提供了关闭事务自动提交的设置,最好在设置连接池时就将其关闭。但C3P0没有提供这一特性,只能依靠spring来设置。
因为JDBC规范规定,当连接对象建立时应该处于自动提交模式,这是跨DBMS的缺省值,如果需要,必须显式的关闭自动提交。C3P0遵守这一规范,让客户代码来显式的设置需要的提交模式。

连接关闭时的是否自动提交

当一个连接关闭时,如果有未提交的事务应该如何处理?JDBC规范没有提及,C3P0默认的策略是回滚任何未提交的事务。这是一个正确的策略,但JDBC驱动提供商之间对此问题并没有达成一致。
C3P0的autoCommitOnClose属性默认是false,没有十分必要不要动它。或者可以显式的设置此属性为false,这样会更明确。

基于注解的声明式事务管理配置
spring-servlet.xml

复制代码
1 <!-- transaction support-->
2 <!-- PlatformTransactionMnager -->
3 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
4     <property name="dataSource" ref="dataSource" />
5 </bean>
6 <!-- enable transaction annotation support -->
7 <tx:annotation-driven transaction-manager="txManager" />
复制代码

还要在spring-servlet.xml中添加tx名字空间

复制代码
 1 ...
 2     xmlns:tx="http://www.springframework.org/schema/tx"
 3     xmlns:aop="http://www.springframework.org/schema/aop"
 4     xsi:schemaLocation="
 5     ...
 6  
 7 http://www.springframework.org/schema/tx
 8  
 9  
10 http://www.springframework.org/schema/tx/spring-tx.xsd
11  
12     ...
复制代码

MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。

另外需要下载依赖包aopalliance.jar放置到WEB-INF/lib目录下。否则spring初始化时会报异常
java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

spring事务特性

spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口

复制代码
1 public interface PlatformTransactionManager {
2  
3   TransactionStatus getTransaction(TransactionDefinition definition)
4     throws TransactionException;
5  
6   void commit(TransactionStatus status) throws TransactionException;
7  
8   void rollback(TransactionStatus status) throws TransactionException;
9 }
复制代码

其中TransactionDefinition接口定义以下特性:

事务隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。

事务只读属性

只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。

spring事务回滚规则

指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。

默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。

还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。

@Transactional注解

@Transactional属性

 
属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

 

用法

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

复制代码
 1 @Transactional(readOnly = true)
 2 public class DefaultFooService implements FooService {
 3  
 4   public Foo getFoo(String fooName) {
 5     // do something
 6   }
 7  
 8   // these settings have precedence for this method
 9   //方法上注解属性会覆盖类注解上的相同属性
10   @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
11   public void updateFoo(Foo foo) {
12     // do something
13   }
14 }

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324754007&siteId=291194637