Spring中事务管理常见问题
1、Spring事务管理
Spring事务管理分为编码式和声明式两种,编程式事务指的是通过编码方式实现事务,声明式事务基于AOP,将具体业务逻辑与事务处理解耦。
声明式事务有两种方式,一种是在配置文件(XML)中做相关的事务规则说明,另一种基于**@Transactional**注解的方式。
2、声明式事务(@Transactional)的使用:
1、xml配置事务:
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
2、使用@Transactional注解
3、@Transactional常见问题
1、@Transactional 只能应用到 public 方法才有效
2、Spring的AOP自调用问题
在Spring的AOP代理下,只有目标方法由外部调用,目标方法才由Spring生成的代理对象来管理。
在同一个类中,一个没有@Transactional的方法调用有@Transactional注解的方法,则注解失效
原因:
Spring在扫描bean的时候,会扫描方法上面是否包含@Transactional注解,如果包含,则spring会为这个bean动态的生成一个子类(即代理类,proxy),代理类继承自原来那个bean。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。 然而,如果该方法是由同一类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过调用方法的那个bean,就不会启动transaction,
导致@Transactional失效。
伪代码:
@Service
class A{
@Transactinal
method b(){...}
method a(){ //标记1
b();
}
}
//Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
class proxy$A{
A objectA = new A();
method b(){ //标记2
startTransaction();
objectA.b();
}
method a(){ //标记3
objectA.a(); //由于a()没有注解,所以不会启动transaction,而是直接调用A的实例的a()方法
}
eg:https://blog.youkuaiyun.com/clementad/article/details/47339519
3.嵌套事务
嵌套事务有一个非常重要的特点是:内层事务依赖外层事务,外层事务失败时会回滚内存事务所做的操作,而内层事务失败则不会引起外层事务回滚。
@Transactional
methodA(){
doSomeThingA();
serviceB.methodB();
doSomeThingB();
}
@Transactional
methodB(){
……
}
在调用methodA时,如果methodB调用失败,则撤回到doSomethingA()的保存点状态,如果methodB执行成功,但此时并没有提交,接着向下执行doSomeThingB()方法,如果doSomeThingB()方法执行失败,则回滚之前所有操作,包括methodB()中的操作。