测试@transactional捕捉异常,回滚情况

测试样例

    @Transactional(rollbackFor = Exception.class)
    public void test(){
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid(getTimerId());
        ticket.setTicketcode(getTimerId());
        int insert = orderingTicketService.insert(ticket);
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid(getTimerId());
        int insert1 = orderingService.insert(ordering);
        System.out.println(insert1);
    }
    //两个实体没有主键冲突 结果 1 1 插入成功
  @Transactional(rollbackFor = Exception.class)
    public void test(){
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid(getTimerId());
        ticket.setTicketcode(getTimerId());
        int insert = orderingTicketService.insert(ticket);
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("202314315680");
        int insert1 = orderingService.insert(ordering);
        System.out.println(insert1);
    }
    // ordering 有主键冲突
    // 结果 1 ordering 报错 ,ticket回滚
    @Transactional(rollbackFor = Exception.class)
    public void test(){
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid(getTimerId());
        ticket.setTicketcode(getTimerId());
        int insert = orderingTicketService.insert(ticket);
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("202314315680");
        int insert1 = 0;
        try {
            insert1 = orderingService.insert(ordering);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(insert1);
    }
     // ordering 有主键冲突
     // 结果 1 0 ordering 错误堆栈打印  ticket没有回滚,插入成功
@Transactional(rollbackFor = Exception.class)
    public void test(){
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid("202334318572");
        ticket.setTicketcode("202334318568");
        int insert = 0;
        try {
           insert = orderingTicketService.insert(ticket); 
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("2023143156801"); 
        int insert1 = orderingService.insert(ordering);
//        int insert1 = 0;
//        try {
//            insert1 = orderingService.insert(ordering);
//        }catch (Exception e){
//            e.printStackTrace();
//        }
        System.out.println(insert1);
    }
    // ticket 有主键冲突 
    // 结果 0 1 ticket有异常堆栈 ordering 插入成功
。 @Transactional(rollbackFor = Exception.class)
    public void test(){
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid("2023343185721");
        ticket.setTicketcode("202334318568");
        int insert = 0;
        try {
           insert = orderingTicketService.insert(ticket);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("2023143156801");
//        int insert1 = orderingService.insert(ordering);
        int insert1 = 0;
        try {
            insert1 = orderingService.insert(ordering);
        }catch (Exception e){
            e.printStackTrace();
            throw new BusinessException(JSONObject.toJSONString(ordering)+"插入失败");
        }
        System.out.println(insert1);
    }
  // ordering 有主键冲突
  // 结果 1 ordering打印堆栈信息 打印BusinessException异常,ticket回滚插入失败
 public void test() {
        try {
            test2();
        } catch (Exception e) {
            log.error("", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void test2() {
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid("20233431857211");
        ticket.setTicketcode("202334318568");
        int insert = 0;
        try {
            insert = orderingTicketService.insert(ticket);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("2023143156801");
//        int insert1 = orderingService.insert(ordering);
        int insert1 = 0;
        try {
            insert1 = orderingService.insert(ordering);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(JSONObject.toJSONString(ordering) + "插入失败");
        }
        System.out.println(insert1);
    }

    // 调用test  ordering 主键冲突
    // 结果1 打印ordering错误堆栈  打印BusinessException错误堆栈 ticket插入成功
@Transactional(rollbackFor = Exception.class)
    public void test() {
        try {
            test2();
        } catch (Exception e) {
            log.error("", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void test2() {
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid("202334318572111");
        ticket.setTicketcode("202334318568");
        int insert = 0;
        try {
            insert = orderingTicketService.insert(ticket);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("2023143156801");
//        int insert1 = orderingService.insert(ordering);
        int insert1 = 0;
        try {
            insert1 = orderingService.insert(ordering);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(JSONObject.toJSONString(ordering) + "插入失败");
        }
        System.out.println(insert1);
    }   
    // 调用test  ordering 主键冲突
    // 结果1 打印ordering错误堆栈  打印BusinessException错误堆栈 ticket插入成功
// 新建service,业务放入其中
@Service
public class TicketDBService {
    @Autowired
    RposOrderingService orderingService;
    @Autowired
    RposOrderingTicketService orderingTicketService;
    @Transactional(rollbackFor = Exception.class)
    public void test2() {
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid("202334318572111");
        ticket.setTicketcode("202334318568");
        int insert = 0;
        try {
            insert = orderingTicketService.insert(ticket);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("2023143156801");
//        int insert1 = orderingService.insert(ordering);
        int insert1 = 0;
        try {
            insert1 = orderingService.insert(ordering);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(JSONObject.toJSONString(ordering) + "插入失败");
        }
        System.out.println(insert1);
    }
}
// 另一个service中调用 
   @Autowired
    TicketDBService ticketDBService;
    public void test() {
        try {
            ticketDBService.test2();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // 调用test  ordering 主键冲突
    // 结果1 打印ordering错误堆栈  打印BusinessException错误堆栈 ticket回滚,插入失败
 // 新建service,业务放入其中
@Service
public class TicketDBService {
    @Autowired
    RposOrderingService orderingService;
    @Autowired
    RposOrderingTicketService orderingTicketService;
    public void test2() {
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid("202334318572111");
        ticket.setTicketcode("202334318568");
        int insert = 0;
        try {
            insert = orderingTicketService.insert(ticket);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("2023143156801");
//        int insert1 = orderingService.insert(ordering);
        int insert1 = 0;
        try {
            insert1 = orderingService.insert(ordering);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(JSONObject.toJSONString(ordering) + "插入失败");
        }
        System.out.println(insert1);
    }
}
// 另一个service中调用 
   @Autowired
    TicketDBService ticketDBService;
    @Transactional(rollbackFor = Exception.class)
    public void test() {
        try {
            ticketDBService.test2();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // 调用test  ordering 主键冲突
    // 结果1 打印ordering错误堆栈  打印BusinessException错误堆栈 ticket插入成功 
    // 内层事务失效
// 新建service,业务放入其中
@Service
public class TicketDBService {
    @Autowired
    RposOrderingService orderingService;
    @Autowired
    RposOrderingTicketService orderingTicketService;
    @Transactional(rollbackFor = Exception.class)
    public void test2() {
        RposOrderingTicket ticket = new RposOrderingTicket();
        ticket.setGroupid("0571001");
        ticket.setOrderid("202334318568");
        ticket.setTicketid("202334318572111");
        ticket.setTicketcode("202334318568");
        int insert = 0;
        try {
            insert = orderingTicketService.insert(ticket);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(insert);

        RposOrdering ordering = new RposOrdering();
        ordering.setGroupid("0571001");
        ordering.setOrderid("2023143156801");
//        int insert1 = orderingService.insert(ordering);
        int insert1 = 0;
        try {
            insert1 = orderingService.insert(ordering);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(JSONObject.toJSONString(ordering) + "插入失败");
        }
        System.out.println(insert1);
    }
}
// 另一个service中调用 
   @Autowired
    TicketDBService ticketDBService;
    @Transactional(rollbackFor = Exception.class)
    public void test() {
        try {
            ticketDBService.test2();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // 调用test  ordering 主键冲突
    // 结果1 打印ordering错误堆栈  打印BusinessException错误堆栈 ticket回滚,插入失败
    // 外加异常org.springframework.transaction.UnexpectedRollbackException: 
    //Transaction rolled back because it has been marked as rollback-only

rolled back because it has been marked as rollback-only异常原因

  1. @Transactional(propagation= Propagation.REQUIRED):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是@Transactional的默认方式。裸的@Transactional注解就是这种方式。在这种情况下,外层事务(ServiceA)和内层事务(ServiceB)就是一个事务,任何一个出现异常,都会在methodA执行完毕后回滚。如果内层事务B抛出异常e(没有catch,继续向外层抛出),在内层事务结束时,spring会把事务B标记为“rollback-only”;这时外层事务A发现了异常e,如果外层事务A catch了异常并处理掉,那么外层事务A的方法会继续执行代码,直到外层事务也结束时,这时外层事务A想commit,因为正常结束没有向外抛异常,但是内外层事务AB是同一个事务,事务B(同时也是事务A)已经被内层方法标记为“rollback-only”,需要回滚,无法commit,这时spring就会抛出org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only,意思是“事务已经被标记为回滚,无法提交”。

rolled back because it has been marked as rollback-only解决方法

methodB和methodA放在同一个service中(不大现实); 直接在外层事务的catch代码块中抛出捕获的内层事务的异常,两层事务有未捕获异常,都回滚(有时候这个异常就是交给外层处理的,抛出到更外层显得多此一举); 在内层事务中做异常捕获处理,并且不向外抛异常,两层事务都不回滚(可以在内层手动做回滚,方法见下面); 最好的方式:如果希望内层事务回滚,但不影响外层事务提交,需要将内层事务的传播方式指定为@Transactional(propagation= Propagation.NESTED),外层事务的提交和回滚能够控制嵌套的内层事务回滚;而内层事务报错时,只回滚内层事务,外层事务可以继续提交。(JPA不支持NESTED,有时可以用REQUIRES_NEW替代一下)。 详细说明参考:https://www.jianshu.com/p/8beab9f37e5b 2019-10-17更新:如果这个异常发生时,内层需要事务回滚的代码还没有执行,则可以@Transactional(noRollbackFor = {内层抛出的异常}.class),指定内层也不为这个异常回滚。

手动回滚

// 回滚整个方法
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// 回滚指定的一段操作
// 设置回滚点
Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
// 回滚到回滚点
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);

不捕捉异常的情况下

A方法中调用B方法

  1. 当A和B在同一个类中时:

  2. A加事务,B加事务,A中报错,事务生效

  3. B加事务,A中报错,事务失效

  4. A加事务,A中报错,事务生效

  5. A加事务,B加事务,B中报错,事务生效

  6. B加事务,B中报错,事务失效

  7. A加事务,B中报错,事务生效

  8. 当A和B不在同一个类中时:

  9. A加事务,B加事务,A中报错,事务生效

  10. B加事务,A中报错,事务失效

  11. A加事务,A中报错,事务生效

  12. A加事务,B加事务,B中报错,事务生效

  13. B加事务,B中报错,事务生效

  14. A加事务,B中报错,事务生效

  15. 总结:

  16. 两方法在不同类

  17. 内层方法报错

  18. 外层方法加事务时:事务一定生效;

  19. 外层方法不加事务,内层方法加事务时,如果满足以下两条件,则事务生效: