1 Micronaut事务管理
1.1 事务传播机制
1.1.1 REQUIRED
Micronaut事务的默认传播机制。
如果当前存在事务,加入当前事务,否则开启一个新的事务。
1.1.2 SUPPORTS
如果当前存在事务,加入当前事务,否则不开事务。
1.1.3 MANDATORY
当前必须存在事务,加入这个事务,否则抛出异常。
1.1.4 REQUIRES_NEW
创建一个新事务,如果已经存在事务,挂起已经存在的事务,先把本事务执行完,然后恢复挂起的外层事务。
提问:
- 如果REQUIRES_NEW开启的内层事务发生回滚,外层事务会继续执行还是回滚? 答:如果回滚是异常造成的,且外层没有捕获异常,外层也会回滚,但如果外层catch了内层的异常,则会执行完。
1.1.5 NOT_SUPPORTED
不创建事务,如果已经存在事务,挂起已经存在的事务,先把本事务执行完,然后恢复挂起的外层事务。
1.1.6 NEVER
不创建事务,如果已经存在事务,抛出异常。
1.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)
1.2 只读事务
在MySQL中,可以用START TRANSACTION READ ONLY开启一个只读事务。
在该事务中如果有对数据库的修改操作(InnoDB, MyISAM, 或其它类型的数据库表),那么将会产生一个错误,并且事务会继续以只读事务进行。
可以对查询加S锁,但加X锁也会报错。
但是在只读事务中,你可以对session级别的临时表进行操作,这是因为这些修改操作和加锁操作对其它事务是不可见的。
只读事务在RR隔离级别下可以保证可重复读,但它的创建速度更快,因为只读事务不需要分配事务ID,也无需为其做回滚的准备。
1.3 事务的开启方式
在Micronaut中,执行SQL时必须显式地开启事务,因为Micronaut在开启事务时获取数据库连接,如果不开启事务,则无法拿到数据库连接。
1.3.1 声明式
- 写事务
@Transactional(value = Transactional.TxType.MANDATORY, rollbackOn = Exception.class) public void functionTRX(int tradeId, int flag) { // ... }
提问:rollbackOn = Exception.class的意义是什么? 答:如果不加这个配置,则事务只会在RuntimeException发生时回滚,如果发生了其它Exception,会出现事务未结束。
- 只读事务
@ReadOnly public void functionTRX(int tradeId, int flag) { // ... }
1.3.2 函数式
dslContext.transaction(configuration -> {
DSLContext dslContext = DSL.using(configuration);
// ...
});
var result= dslContext.transactionResult(configuration -> {
DSLContext dslContext = DSL.using(configuration);
return ...;
});
1.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);
}
}