同一个代理类里面,被调用方法有@Transactional注解,而外层防范没有注解
@Override
public void test1() {
test2();
}
@Transactional
public void test2(){
try {
... sql操作 ...
System.out.println(1/0) ; //抛出异常
} catch (Exception e) {
// 发生异常时手动触发事务回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
在上述代码中事务不仅不会回滚,还会报出异常org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope
事实上Spring中的@Transactional原理是通过TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截。像上面的代码,Spring AOP实际上是为每个@Service注解的类生成一个代理类,由于代理类的test1方法没有被注解修饰,Spring认为这里不用事务拦截器进行拦截,即便被调用类中使用了事务注解。
如果想要事务回滚生效,需要将子方法的事务控制交给调用的方法来处理:
方案1:子方法中不用 try-catch 处理运行异常
方案2:子方法的catch里面将运行异常抛出【throw new RuntimeException();】
默认情况下,Spring会对unchecked异常进行事务回滚,也就是默认对 RuntimeException() 异常或是其子类进行事务回滚。
如果是checked异常则不回滚,例如空指针异常、算数异常等会被回滚;文件读写、网络问题Spring就没法回滚。
若想对所有异常(包括自定义异常)都起作用,注解上面需配置异常类型:@Transactional(rollbackFor = Exception.class)
数据库要支持事务,如果是mysql,要使用innodb引擎,myisam不支持事务
事务@Transactional由spring控制时,它会在抛出异常的时候进行回滚。如果自己使用try-catch捕获处理了,是不生效的。如果想事务生效可以进行手动回滚或者在catch里面将异常抛出【throw new RuntimeException();】
方案一:手动抛出运行时异常(缺陷是不能在catch代码块自定义返回值)
try{
....
}catch(Exception e){
logger.error("",e);
throw new RuntimeException(e);
}
方案二:手动进行回滚【 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 】
try{
...
}catch(Exception e){
log.error("fail",e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}
import javax.transaction.Transactional;
// 和
import org.springframework.transaction.annotation.Transactional; // 推荐
这两个都可以用,对比了一下他们两个的方法和属性,发现后面的比前面的强大。建议使用后面的。