Micronaut事务管理

Micronaut事务管理

1 事务传播机制

1.1 REQUIRED

Micronaut事务的默认传播机制。

如果当前存在事务,加入当前事务,否则开启一个新的事务。

1.2 SUPPORTS

如果当前存在事务,加入当前事务,否则不开事务。

1.3 MANDATORY

当前必须存在事务,加入这个事务,否则抛出异常。

1.4 REQUIRES_NEW

创建一个新事务,如果已经存在事务,挂起已经存在的事务,先把本事务执行完,然后恢复挂起的外层事务。

提问:

  • 如果REQUIRES_NEW开启的内层事务发生回滚,外层事务会继续执行还是回滚? 答:如果回滚是异常造成的,且外层没有捕获异常,外层也会回滚,但如果外层catch了内层的异常,则会执行完。

1.5 NOT_SUPPORTED

不创建事务,如果已经存在事务,挂起已经存在的事务,先把本事务执行完,然后恢复挂起的外层事务。

1.6 NEVER

不创建事务,如果已经存在事务,抛出异常。

1.7 调用带事务的存储过程

@Transactional(rollbackOn = Exception.class)
public void functionTRX(int tradeId, int flag) {
   dslContext.update(A)
      .set(A.a, 1)
      .where(A.id.eq(tradeId))
      .execute(); // (1)

   dslContext.fetch("CALL SP_WITH_TRANSACTION(?)", tradeId); // (2)

   dslContext.insertInto(B, B.b1, B.b2).values(1,2).execute(); // (3)
}

提问:

  • (2)中ROLLBACK,(1)会被回滚吗? 答:不会,因为存储过程中的事务开启时,把(1)提交了。
  • (3)发生异常,哪些语句被回滚了? 答:只有(3)。
  • 所以上述方法的事务是怎样被执行的? (1)事务执行,(2)开启事务时(1)被提交,(2)提交后新事务执行(3)

2 只读事务

在MySQL中,可以用START TRANSACTION READ ONLY开启一个只读事务。

在该事务中如果有对数据库的修改操作(InnoDB, MyISAM, 或其它类型的数据库表),那么将会产生一个错误,并且事务会继续以只读事务进行。

可以对查询加S锁,但加X锁也会报错。

但是在只读事务中,你可以对session级别的临时表进行操作,这是因为这些修改操作和加锁操作对其它事务是不可见的。

只读事务在RR隔离级别下可以保证可重复读,但它的创建速度更快,因为只读事务不需要分配事务ID,也无需为其做回滚的准备。

3 事务的开启方式

在Micronaut中,执行SQL时必须显式地开启事务,因为Micronaut在开启事务时获取数据库连接,如果不开启事务,则无法拿到数据库连接。

3.1 声明式

  1. 写事务
    @Transactional(value = Transactional.TxType.MANDATORY, rollbackOn = Exception.class)
    public void functionTRX(int tradeId, int flag) {
       // ...
    }
    

    提问:rollbackOn = Exception.class的意义是什么? 答:如果不加这个配置,则事务只会在RuntimeException发生时回滚,如果发生了其它Exception,会出现事务未结束。

  2. 只读事务
    @ReadOnly
    public void functionTRX(int tradeId, int flag) {
       // ...
    }
    

3.2 函数式

dslContext.transaction(configuration -> {
   DSLContext dslContext = DSL.using(configuration);
   // ...
});
var result= dslContext.transactionResult(configuration -> {
   DSLContext dslContext = DSL.using(configuration);
   return ...;
});

3.3 手动开启

@Inject
private SynchronousTransactionManager<Connection> trxMgr;

void functionTRX() {
    TransactionStatus<Connection> trx = trxMgr.getTransaction(TransactionDefinition.READ_ONLY);
    try{    
    trxMgr.commit(trx);
    } catch (Exception e) {
    trxMgr.rollback(trx);
    }
}