Spring中@Transactional用法
在spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项的使用。
1. @Transactional的定义
Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题。在现实中,实际的问题往往比我们预期的要复杂很多,这就要求对@Transactional有深入的了解,以来应对复杂问题。
首先我们来看看@Transactional的代码定义:
1 引言: 在spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项的使用。 2 3 1. @Transactional的定义 4 5 Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题。在现实中,实际的问题往往比我们预期的要复杂很多,这就要求对@Transactional有深入的了解,以来应对复杂问题。 6 7 首先我们来看看@Transactional的代码定义: 8 9 [html] view plain copy 在CODE上查看代码片派生到我的代码片 10 @Target({ElementType.METHOD, ElementType.TYPE}) 11 @Retention(RetentionPolicy.RUNTIME) 12 @Inherited 13 @Documented 14 public @interface Transactional { 15 16 /** 17 * A qualifier value for the specified transaction. 18 * <p>May be used to determine the target transaction manager, 19 * matching the qualifier value (or the bean name) of a specific 20 * {@link org.springframework.transaction.PlatformTransactionManager} 21 * bean definition. 22 */ 23 String value() default ""; 24 25 /** 26 * The transaction propagation type. 27 * Defaults to {@link Propagation#REQUIRED}. 28 * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior() 29 */ 30 Propagation propagation() default Propagation.REQUIRED; 31 32 /** 33 * The transaction isolation level. 34 * Defaults to {@link Isolation#DEFAULT}. 35 * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel() 36 */ 37 Isolation isolation() default Isolation.DEFAULT; 38 39 /** 40 * The timeout for this transaction. 41 * Defaults to the default timeout of the underlying transaction system. 42 * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout() 43 */ 44 int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; 45 46 /** 47 * {@code true} if the transaction is read-only. 48 * Defaults to {@code false}. 49 * <p>This just serves as a hint for the actual transaction subsystem; 50 * it will <i>not necessarily</i> cause failure of write access attempts. 51 * A transaction manager which cannot interpret the read-only hint will 52 * <i>not</i> throw an exception when asked for a read-only transaction. 53 * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly() 54 */ 55 boolean readOnly() default false; 56 57 /** 58 * Defines zero (0) or more exception {@link Class classes}, which must be a 59 * subclass of {@link Throwable}, indicating which exception types must cause 60 * a transaction rollback. 61 * <p>This is the preferred way to construct a rollback rule, matching the 62 * exception class and subclasses. 63 * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)} 64 */ 65 Class<? extends Throwable>[] rollbackFor() default {}; 66 67 /** 68 * Defines zero (0) or more exception names (for exceptions which must be a 69 * subclass of {@link Throwable}), indicating which exception types must cause 70 * a transaction rollback. 71 * <p>This can be a substring, with no wildcard support at present. 72 * A value of "ServletException" would match 73 * {@link javax.servlet.ServletException} and subclasses, for example. 74 * <p><b>NB: </b>Consider carefully how specific the pattern is, and whether 75 * to include package information (which isn't mandatory). For example, 76 * "Exception" will match nearly anything, and will probably hide other rules. 77 * "java.lang.Exception" would be correct if "Exception" was meant to define 78 * a rule for all checked exceptions. With more unusual {@link Exception} 79 * names such as "BaseBusinessException" there is no need to use a FQN. 80 * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)} 81 */ 82 String[] rollbackForClassName() default {}; 83 84 /** 85 * Defines zero (0) or more exception {@link Class Classes}, which must be a 86 * subclass of {@link Throwable}, indicating which exception types must <b>not</b> 87 * cause a transaction rollback. 88 * <p>This is the preferred way to construct a rollback rule, matching the 89 * exception class and subclasses. 90 * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)} 91 */ 92 Class<? extends Throwable>[] noRollbackFor() default {}; 93 94 /** 95 * Defines zero (0) or more exception names (for exceptions which must be a 96 * subclass of {@link Throwable}) indicating which exception types must <b>not</b> 97 * cause a transaction rollback. 98 * <p>See the description of {@link #rollbackForClassName()} for more info on how 99 * the specified names are treated. 100 * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)} 101 */ 102 String[] noRollbackForClassName() default {}; 103 104 }
基于源代码,我们可以发现在@Transactional,原来有这么多的属性可以进行配置,从而达到复杂应用控制的目的。具体各个属性的用法和作用,将在本文的后面逐一进行讲解和说明。
2. 使用@Transactional的Spring配置
为了使用基于@Transactional的事务管理,需要在Spring中进行如下的配置:
1 <beans:bean id="transactionManager" 2 class="org.springframework.orm.jpa.JpaTransactionManager"> 3 <beans:property name="dataSource" ref="dataSource" /> 4 <beans:property name="entityManagerFactory" ref="entityManagerFactory" /> 5 </beans:bean> 6 7 <!-- 声明使用注解式事务 --> 8 <tx:annotation-driven transaction-manager="transactionManager" />
dataSource是在Spring配置文件中定义的数据源的对象实例,EntityManagerFactory是基于JPA使用的实体类管理器:org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean。这些都是用来配置与数据库的连接信息,本质上,@Transactional使用了JDBC的事务来进行事务控制的。
<annotation-driven>标签的声明,则是在Spring内部启用@Transactional来进行事务管理,类似开关之类的声明。
3. @Transactional之value
value这里主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2.
然后,用户可以根据这个参数来根据需要指定特定的txManager.
那有同学会问什么情况下会存在多个事务管理器的情况呢? 比如在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的。
4. @Transactional之propagation
Propagation支持7种不同的传播机制:
- REQUIRED
业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为.。
- SUPPORTS:
如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
- MANDATORY:
只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常
- REQUIRES_NEW
业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行.
- NOT_SUPPORTED
声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行.
- NEVER:
声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
- NESTED:
如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效.
其实大家最感到困惑的是REQUIRED_NEW和NESTED两种不同的传播机制,功能类似,都涉及到了事务嵌套的问题,那两者有何区别呢?该如何正确使用这两种模式呢?
隔离级别所要解决的问题是在应用程序中,存在多个事务同时在运行之时,需要解决和处理好的问题。那首先来看看,一般会出现哪些问题呢?先来看看吧。
- 脏读(dirty read)
- 不可重复读(non-repeatable read)
- 幻读 (phantom read)
下面来看看Spring中@Transactional中Isolation有具备的值:
- DEFAULT 使用各个数据库默认的隔离级别
- Read Uncommited :读未提交数据( 会出现脏读,不可重复读,幻读 )
- Read Commited :读已提交的数据(会出现不可重复读,幻读)
- Repeatable Read :可重复读(会出现幻读)
- Serializable :串行化
其与事务中容易出现的问题对应关系具体如下:
具体如何来设置具体的隔离界别,则依据业务系统具体可以容忍的程度而定。Serializable最为严格,然而效率最低;Read Uncommited效率最高,但是容易出现各种问题,中间的2个级别介于二者之间,故本质上是效率与出错概率的平衡与妥协。
6. @Transactional之timeout
用于设置事务处理的时间长度,阻止可能出现的长时间的阻塞系统或者占用系统资源。单位为秒。如果超时设置事务回滚,并抛出TransactionTimedOutException异常。
spring事务超时 = 事务开始时到最后一个Statement创建时时间 + 最后一个Statement的执行时超时时间(即其queryTimeout)。
关于事务超时时间的计算可以参考:http://jinnianshilongnian.iteye.com/blog/1986023
7. @Transactional之readOnly
默认情况下是false,可以显示指定为true, 告诉程序该方法下使用的是只读操作,如果进行其他非读操作,则会跑出异常;这个紧紧适用于只有readOnly标识的情况下,当其与propagation机制同时使用之时,则会出现只读设置被覆盖的情况,比如在required的情况下。在使用 REQUIRED 传播模式时,会抛出一个只读连接异常。使用 JDBC 时是这样。使用基于 ORM 的框架时,只读标志只是对数据库的一个提示,并且一条基于 ORM 框架的指令(本例中是 hibernate)将对象缓存的 flush 模式设置为 NEVER,表示在这个工作单元中,该对象缓存不应与数据库同步。不过,REQUIRED 传播模式会覆盖所有这些内容,允许事务启动并工作,就好像没有设置只读标志一样。
具体的详细内容可以参考: http://robinsoncrusoe.iteye.com/blog/825531
8. @Transactional之rollbackForClassName/rollbackFor
Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked,如果遇到checked意外就不回滚。
用来指明回滚的条件是哪些异常类或者异常类名。用法比较简单,这些不再赘述。
9. @Transactional之noRollbackForClassName/noRollbackFor作用雷同于8, 用来指明在抛出特定异常的情况下,不进行数据库的事务回滚操作。
10. 总结
在针对@Transactional的各个属性进行详细分析之后,相信各位对他们具体的含义和应用场景有了一定的认识,使用起来便会不再那么迷茫和犹豫;非常感谢Spring给我们提供了这些便捷的事务管理方式,摆脱了繁琐的体力活,提升了开发的效率。