maven 打包失败
一.问题描述:
业务逻辑上存在事务嵌套的情况。例如:方法methodA()中有methodB(),两个方法都有操作数据库的业务代码insert()。执行methodA后,在一定场景下抛出了异常,异常如下:
Transaction rolled back because it has been marked as rollback-only
代码如下:
@Service
public class A{
@Transactional(rollbackFor = Exception.class)
public void methodA(){
try{
methodB();
insert();
}catch(Exception e){
System.out.println("异常抛出");
}
}
}
@Service
public class B{
@Transactional(rollbackFor = Exception.class)
public void methodB(){
insert();
System.out.println(1 / 0);
}
}
二.原因分析:
当spring开启事务时,如果不修改propagation的参数,则默认是propagation.REQUIRED。即如果有没有事务则新启一个事务,如果已经存在事务则加入这个事务。
当内层事务异常的情况下,如果是这种传播方式,正常来讲是需要回滚的,但是spring给内层事务做了一个rollback的标记。所以当内层事务抛出的异常被外层try-catch时,外层事务正常执行,但在最后提交的时候发现,内层被标记了rollbck,就会抛出Transaction rolled back because it has been marked as rollback-only这个异常。
三.解决方法:
分三种种业务情况来处理这个问题。
第一种情况:
内层事务异常的情况下只回滚内层事务,但不影响外层事务提交。这时只要修改内层事务的事务传播方式,@Transactional注解内加上propagation = Propagation.REQUIRES_NEW就可以。
代码如下:
@Service
public class A{
@Transactional(rollbackFor = Exception.class)
public void methodA(){
try{
methodB();
insert();
}catch(Exception e){
System.out.println("异常抛出");
}
}
}
@Service
public class B{
@Transactional(rollbackFor = Exception.class,,propagation = Propagation.REQUIRES_NEW)
public void methodB(){
insert();
System.out.println(1 / 0);
}
}
第二种情况:
methodB方法不是通用的方法,内层异常的情况下,回滚全部事务,让内层事务抛出的异常被外层事务的try–catch处理,设置手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
或者外层不要try–catch处理,异常直接抛到最外层。
代码如下:
@Service
public class A{
@Transactional(rollbackFor = Exception.class)
public void methodA(){
try{
methodB();
insert();
}catch(Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}
@Service
public class B{
@Transactional(rollbackFor = Exception.class)
public void methodB(){
insert();
System.out.println(1 / 0);
}
}
第三种种情况:
methodB方法是通用的方法,只是在这个地方使用,那么直接把内层的事务去了,让它和外层合并成一个事务也能解决这个问题。
代码如下:
@Service
public class A{
@Transactional(rollbackFor = Exception.class)
public void methodA(){
try{
methodB();
insert();
}catch(Exception e){
System.out.println("异常抛出");
}
}
}
@Service
public class B{
public void methodB(){
insert();
System.out.println(1 / 0);
}
}
四.特殊情况:
methodA和methodB在同一个类中,那就不会出现Transaction rolled back because it has been marked as rollback-only这个问题。
代码如下:
@Service
public class A{
@Transactional(rollbackFor = Exception.class)
public void methodA(){
try{
methodB();
insert();
}catch(Exception e){
System.out.println("异常抛出");
}
}
@Transactional(rollbackFor = Exception.class)
public void methodB(){
insert();
System.out.println(1 / 0);
}
}
}
本质区别在于:methodA调用了同一个类的方法methodA。
原因:因为注解的生效是通过代理模式实现的,同一个类下相互调用,被调用者是没法生成代理方法的,即method2的事务根本就没有生效。所以也不会出现异常。