目录
- @Transactional 普通例子代码和测试输出
- 编程式事务
- 事务代理实现和TransactionAspectSupport重要类
- 复习Spring的事务传播机制有哪些
- 实际工作中用到的事务处理
@Transactional事务原理理解
@Transactional 普通例子代码和测试输出
@Transactional(rollbackFor = Exception.class)
@PutMapping(value = "/test/oom")
public Boolean testOom() throws Exception {
TbUser tbUser = new TbUser();
tbUser.setUserid("abc");
tbUser.setPassword("123456");
tbUser.setName("abc");
tbUser.setPhone("abc");
tbUser.setAddress("abc");
try {
List<TbUser> tbUserList = tbUserService.getAllUser();
System.out.println("users size1:" + tbUserList.size());
tbUserService.insert(tbUser);
tbUserList = tbUserService.getAllUser();
System.out.println("users size2:" + tbUserList.size());
// 继续业务处理,模拟异常
int c = 1 / 0;
return true;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
System.out.println("finally delete");
}
}
测试输出如下
数据库去查询确实会滚了
编程式事务
如下为一种写法,事务的提交和会滚自己控制,借助spring的PlatformTransactionManager
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private JdbcTemplate jdbcTemplate;
@PutMapping(value = "/test/transaction")
public Boolean testTransaction() throws Exception {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus transactionStatus = platformTransactionManager.getTransaction(def);
try {
// 处理业务
jdbcTemplate.update("insert into tb_user (name, phone, address) values (?, ?, ?)", "jack", "12345", "city");
int c = 1 / 0;
// 提交事务
System.out.println("transaction commit...");
platformTransactionManager.commit(transactionStatus);
return true;
} catch (Exception e) {
e.printStackTrace();
// 事务回滚
System.out.println("transaction rollback..." + transactionStatus.isCompleted());
platformTransactionManager.rollback(transactionStatus);
return false;
}
}
事务代理实现和TransactionAspectSupport重要类
直接跟随上方异常栈去走debug代码
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
中,创建一个事务
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing
进行了事务回滚
/**
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
debug到这里,然后Evaluate可以获取到两条记录
回滚完成后就只有一条记录:
另外可以看到事务的配置信息
- Spring事务传播方式是PROPAGATION_REQUIRED,即如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这也是最常见的选择。
- 隔离级别,使用数据库默认的(mysql默认隔离级别是REPEATABLE READ可重复读)
回滚规则属性中也能看到设定的回滚规则:
复习Spring的事务传播机制有哪些
完全背不下来,直接理解记忆。
考虑如下代码的doLogic方法
class AService{
@Autowired
priavte BService bService;
public void doLogic(){
aaa();
bService.bbb();
ccc()
}
}
可以看成如下,如果有事务,则是 有嵌套的,但是到底要不要,则可以自由决定
begin
aaa()
--------------
begin
bbb()
commit();
--------------
ccc()
commit();
比如:bService里面可以
- 不要事务
- 要事务且用aService的事务(如果有)
- 要事务自己开启全新事务(这样里面就有三种不同的处理方式了)
对于aService:
- 本身没有事务
- 有事务, 如果有异常如何会滚:回滚自己的,还是把bService一起滚掉等等场景
这么一结合确实场景很多,Spring给出了如下的7种:
分别是:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER和NESTED。
REQUIRED:如果当前存在事务,则加入该事务;如果不存在事务,则创建一个新的事务。这是Spring的默认传播机制,适用于大多数业务场景,确保多个事务操作在同一个事务中执行,以保持数据一致性
。
SUPPORTS:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行。适用于那些不需要事务也能正确执行,但在事务环境下运行也不会造成任何问题的方法
。
MANDATORY:必须在一个已存在的事务中执行,如果当前没有事务则抛出异常。适用于那些必须在事务中执行的操作
。
REQUIRES_NEW:总是创建一个新的事务,如果当前存在事务,则将当前事务挂起,新的事务结束后,再恢复之前被挂起的事务。适用于那些需要在独立事务中执行的操作
。
NOT_SUPPORTED:不使用事务,如果当前存在事务,则将其挂起,直到该方法完成后再继续执行。适用于那些不需要事务的方法
。
NEVER:不允许在事务中执行,如果当前存在事务,则抛出异常。适用于那些必须在非事务环境中执行的操作
。
NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则新建一个事务。适用于需要嵌套事务的场景
。
实际工作中用到的事务处理
- dbOperate做了一系列数据库操作,必须再次之后进行callBack方法
method(){
dbOpeate()
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
@Override
public void afterCompletion(int status) {
callBack();
}
});
}
- 新事务处理完成后,再处理业务逻辑
@Transactional(rollbackFor = Exception.class)
void method(){
try
bService.getLock();
cService.dbOperation();
}finally{
bService.releaseLock();
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
void getLock(){
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
void releaseLock(){
}
执行getLock和releaseLock是挂起method,自己开启新事务快速处理完成;因为数据库操作简单且快,不至于和后面的dbOperation产生模糊关系。
- 使用事务事件监听
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
一些列复杂操作,在事务前后有特定的操作,如统计、告知等