带演示案例的声明式事务管理详解!一次搞定声明式事务。

199 阅读3分钟

通过枚举类查询所有的事务传播类型, Spring 定义了一个事务传播的枚举类:Propagation

public enum Propagation {
    // 常用于增删改
    // 调用方有事务,被调用方也有事务,则被调用方加入调用方共用一个事务
    // 也是事务注解默认的使用方式
    REQUIRED(0),
    // 常用于查询。
    // 事务的传播方式是跟着外层方法走的,如果外层没有事务,则不使用事务
    SUPPORTS(1),
    // 强制的,谁调用了我,调用方就必须要有一个事务,如果没有事务就抛出异常
    MANDATORY(2),
    // 调用方有事务,则挂起该事务,并且创建一个新的事务给自己使用;
    // 调用方没有事务,则同REQUIRED
    REQUIRES_NEW(3),
    // 如果调用方有事务则把事务挂起,自己不使用事务去运行数据库操作
    // 常用查询操作
    NOT_SUPPORTED(4),
    // 以没有事务的形式去执行,如果发现调用方有事务存在则抛出异常
    NEVER(5),
    // 调用方有事务,开启嵌套事务,否则同REQUIRED
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

栗子:

//演示事务传播
// demo1 ,演示REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
    // 事务会往下传递,saveChildren方法里面发生异常后,saveParent(),saveChildren()都会回滚
    // 数据库不会有任何数据插入成功
    stuService.saveParent();
    stuService.saveChildren();
}

// demo2,演示调用方没有事务,子方法有事务
@Override
public void testPropagationTrans() {
    // saveParent会保存到数据库里面去,而saveChildren发生异常后会进行回滚,因为saveChildren使用了
    // REQUIRED事务传播
    stuService.saveParent();
    stuService.saveChildren();
}
@Override
public void saveParent() {
    Stu stu = new Stu();
    stu.setName("parent");
    stu.setAge(19);
    stuMapper.insert(stu);
}
// 调用方没有事务,创建新的事务,saveChild1,saveChild2所处同一事务中,异常后都会回滚
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveChildren() {
    saveChild1();
    int a = 1 / 0;
    saveChild2();
}

// demo3,演示SUPPORTS。子方法依靠外层方法的事务,外层有事务就使用,没有就不使用
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
    stuService.saveParent();
    stuService.saveChildren();
}
// 子方法
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void saveChildren() {
    saveChild1();
    int a = 1 / 0;
    saveChild2();
}

// demo4,演示MANDATORY。强制的,调用方必须拥有事务否则报错
@Override
public void testPropagationTrans() {
    stuService.saveParent();
    stuService.saveChildren();
}
@Transactional(propagation = Propagation.MANDATORY)
@Override
public void saveChildren() {
    saveChild1();
    int a = 1 / 0;
    saveChild2();
}
// 如果调用方没有事务,则会报错
org.springframework.transaction.IllegalTransactionStateException: 
No existing transaction found for transaction marked with propagation 'mandatory'


//demo5 演示 REQUIRES_NEW 调用方没有事务的情况
@Override
public void testPropagationTrans() {
    // saveChildren发生异常会进行回滚,而saveParent会保存成功
    stuService.saveParent();
    stuService.saveChildren();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void saveChildren() {
    saveChild1();
    int a = 1 / 0;
    saveChild2();
}

// demo 6 ,演示REQUIRES_NEW 调用方有事务的情况,且调用方发生了异常
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
    // saveParent会回滚,saveChildren不会回滚
    // 因为saveChildren的REQUIRES_NEW,会在父级方法有事务的时候,创建一个新的事务去使用
    stuService.saveParent();
    stuService.saveChildren();
    int a = 1 / 0;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void saveChildren() {
    saveChild1();
    //int a = 1 / 0;
    saveChild2();
}

// demo 7  演示NOT_SUPPORTED
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
    // saveChildren抛出异常没有回滚,但是saveParent需要被回滚
    stuService.saveParent();
    stuService.saveChildren();
    //int a = 1 / 0;
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void saveChildren() {
    saveChild1();
    int a = 1 / 0;
    saveChild2();
}

// demo 8 演示never
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
    // 会抛出异常,因为调用方拥有事务
    stuService.saveParent();
    stuService.saveChildren();
    //int a = 1 / 0;
}
org.springframework.transaction.IllegalTransactionStateException: 
Existing transaction found for transaction marked with propagation 'never'

// demo9 演示被调用方使用NESTED,调用方使用REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
    stuService.saveParent();
    stuService.saveChildren();
    // 调用方发生异常,那么saveChildren方法是跟着调用方一起回滚的
    int a = 1 / 0;
}

@Transactional(propagation = Propagation.NESTED)
@Override
public void saveChildren() {
    saveChild1();
    // int a = 1 / 0;
    saveChild2();
}

@Transactional 的使用注意事项总结

  1. @Transactional 注解只有作用到 public 方法上事务才生效
  2. 底层使用的数据库必须支持事务机制,否则不生效。比如使用 MySQL ,innodb 引擎支持事务,myisam 引擎就不支持
  3. 一般常用的事务传播方式,查询使用SUPPORTS,增删改使用REQUIRED