声明性事务的使用步骤
1.声明性事务需要spring-tx的包。它依赖于AOP,还需要导入AOP相关的包。
1 | org.springframework.spring-tx |
2.配置事务管理器,事务管理器需要dataSource。开启事务,需要aop和tx名称空间。
1 | <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
3.在需要事务控制的方法上添加@Transactional注解。
事务的属性
timeout:超时设置,时间单位为秒。当事务执行时间>timeout,事务还未执行结束,会抛出超时异常,避免长事务的产生。需要注意的一点是,如果有多个事务,需要把timeout设置在大事务上才有作用。如下所示,timeout属性应该设置在事务A这个大事务才生效。
1
2
3
4
5
6
73) (propagation = Propagation.REQUIRED,timeout =
public void A(){
// B事务,REQUIRES_NEW
B();
// C事务,REQUIRES_NEW
C();
}readOnly:设置事务为只读事务,当数据库操作只有读操作时,可以设置为true,提高效率。
noRollbackFor:默认发生运行时异常和Error事务会回滚。可以通过noRollbackFor指定哪些运行时异常不回滚。参数是一个class数组。
1
.class}) (noRollbackFor = {NullPointerException
noRollbackForClassName:与noRollbackFor的区别在于通过类名指定哪些运行时异常不回滚。参数是一个string数组。
1
"NullPointerException.class"}) (noRollbackForClassName = {
rollBackFor:指定哪些checked异常可以回滚
noRollbackForClassName:通过类名指定哪些checked异常可以回滚
isolation:指定事务隔离级别
1
(isolation = Isolation.REPEATABLE_READ)
事务传播行为
当涉及到多个事务时,事务传播行为就是规定事务间的多个关系的。常用的有REQUIRED(同一辆车,翻车一起翻)、REQUIRES_NEW(自己开一辆车,别人翻车,与我物无关)。
- REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
- REQUIRES_NEW:如果存在一个事务,则将当前事务挂起,重新开启一个新的事务。
下面举几个栗子说明:
栗子1:同是REQUIRED
大事务addOrder包含了两个小事务,传播行为都是REQUIRED。三个事务都是在同一辆车上,如果任何一个翻车了,三个事务全部回滚。如下,大事务抛出了运行时异常,insert()和update()方法都会回滚。
1 |
|
栗子2:REQUIRED中包含REQUIRES_NEW,子事务不发生异常
大事务addOrder()中包含两个小事务。addOrder()和update()事务在同一辆车上,insert()自己开了一辆车。现在addOrder()发生了运行时异常,翻车了。addOrder()和update()事务在同一辆车上,都会回滚操作。但是insert()自己开的车不受影响,会插入成功。
1 |
|
栗子3:REQUIRED中包含REQUIRES_NEW,子事务发生异常
大事务addOrder()中包含两个小事务。addOrder()和update()事务在同一辆车上,insert()自己开了一辆车。现在insert()发生了运行时异常,翻车了。按理说,insert()自己翻车,并不影响addOrder()和update()这辆车,也就是update会执行成功。但结果是insert()和update()都回滚了,原因在于insert发生了异常,异常往上抛出,到了addOrder,addOrder()也发生了运行时异常,导致update()回滚。
1 |
|
如果,子事务insert()不往上抛出异常,而是子事务内try..catch..,运行结果又是怎样的呢?
insert()和update()都执行成功了。因为insert()自己捕获了运行时异常,insert()的事务不回滚。同时也不会将异常向上抛出,update()也会执行成功。
栗子4:同是REQUIRES_NEW,子事务不发生异常
大事务addOrder()启动时创建一个事务,两个子事务也是自身启动一个事务,与大事务互不影响。大事务翻车,子事务不受影响,insert()和update()都执行成功。
1 | (propagation = Propagation.REQUIRES_NEW) |
如果,insert()发生异常,异常往上抛,addOrder也发生了运行时异常。insert()会回滚,而update()都没机会执行,因为还没执行到update(),addOrder就回滚了。
栗子5:同是REQUIRES_NEW,子事务发生异常
如果,insert()发生异常,异常往上抛,addOrder也发生了运行时异常。insert()会回滚,而update()都没机会执行,因为还没执行到update(),addOrder就回滚了。
1 | (propagation = Propagation.REQUIRES_NEW) |
如果,update()发生异常,异常会往上抛。addOrder也发生了运行时异常。update()肯定会回滚,而insert()方法不会回滚,因为insert()是自己开的一辆车,它会执行成功。
1 | (propagation = Propagation.REQUIRES_NEW) |
但是如果insert()是REQUIRED的传播行为,受到addOrder运行时异常的影响,insert()也会回滚? 实际上,这里会发生死锁,insert()与addOrder是同一个事务,插入数据之后,addOrder事务还没有提交,未释放锁,此时update()去更新需要获取到锁,就发生了死锁。
总结:不管哪一个事务发生了异常,在往上抛的时候,只要是REQUIRES_NEW的事务都会执行成功(不会回滚)
本类事务方法方法之间的调用是同一个事务
addOrder大事务中包含了insert()和update()两个子事务,两个子事务都是REQUIRES_NEW。按理说,addOrder事务发生运行时异常,并不会影响到insert()和update()两个子事务,insert()和update()应该执行成功。
但是,由于这是在本类方法中,都是使用的同一个代理对象,实际上是同一个事务…
只要有一个方法发生了异常,事务都会回滚。
1 | (propagation = Propagation.REQUIRED) |
xml配置事务
1 | <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
一般情况下,使用xml配置事务管理器时,可以使用*来切入pointCut下的所有方法,而只读方法可以设置readOnly=true来提高性能。
1 | <tx:attributes> |