使用@Transactional需要注意的问题
1、@Transactional 只能应用到 public 方法才有效
只有@Transactional 注解应用到 public 方法,才能进行事务管理。
这是因为在使用 Spring AOP 代理时,Spring 在调用在图中的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取 @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。
AbstractFallbackTransactionAttributeSource类:
protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;}
这个方法会检查目标方法的修饰符是不是 public,若不是 public,就不会获取@Transactional 的属性配置信息,最终会造成不会用 TransactionInterceptor 来拦截该目标方法进行事务管理。
2、避免 Spring 的 AOP 的自调用问题
在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理。
这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。因为方法的调用是通过当前对象来调用(自己调用自己),而不是通过代理对象来调用。
BookService类
package study.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import study.dao.BookDao;
@Service
public class BookService {
@Autowired
BookDao bookDao;
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String username,int id){
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
System.out.println("嵌套事务 checkout: " + TransactionSynchronizationManager.getCurrentTransactionName());
// try {
int i=1/0;
// }catch (Exception e){
// System.out.println(e.getMessage()); }
}
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(int id,int bookPrice){
bookDao.updatePrice(id,bookPrice);
System.out.println("嵌套事务 updatePrice: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
// @Transactional
public void mult(){
System.out.println("嵌套事务 mult: " + TransactionSynchronizationManager.getCurrentTransactionName());
checkout("zhangsan",3);
updatePrice(1,200);
}
}
@Test
public void test03(){
ApplicationContext context = new
ClassPathXmlApplicationContext("ApplicationContext.xml");
BookService bookService = context.getBean("bookService", BookService.class);
bookService.mult();
}
运行,控制台输出:
嵌套事务 mult: null
嵌套事务 checkout: null
java.lang.ArithmeticException: / by zero
刷新Navicat Lite,发现都更新了
上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。为解决这两个问题,使用 AspectJ 取代 Spring AOP 代理。
原因、解决:https://www.letianbiji.com/spring/spring-aop-proxy-self-call.html
https://www.huaweicloud.com/articles/7b6692fca13f7b5c026f8f2db07f5718.html 分析到:
springAOP实例 BookService$$Cglib 继承自extend BookService,
当执行到BookService$$Cglib.mult()时,
因为BookService$$Cglib没有重写checkout(),updatePrice()方法,所以会调用 super.checkout(),super.updatePrice()。但是父类的checkout(),updatePrice()方法是未增强过的,所以事务管理失效了。