Spring提供了@Transactional(rollbackFor = Exception.class)声明式事务,他非常的简单且好用,但是也有他失效的时候比如:
在多线程中 私有方法中 service中调用自己的方法这几种方式都会导致事务失效. 或者一些特殊的业务场景声明式事务无法满足
这时就要用Spring编程式事务编程式事务. 在这里主要介绍两种TransactionTemplate与DataSourceTransactionManager
上代码:
/**
* 测试编程式事务
*
* @throws Exception 异常
*/
@Test
public void testTransaction1() throws Exception {
// 获取默认的事务
TransactionStatus transactionStatus = this.dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
try {
SysUser sysUser = new SysUser();
sysUser.setUsername(RandomStringUtils.randomAlphanumeric(6));
sysUser.setNickname("22223");
sysUser.setPassword("33333");
sysUser.setSalt("123123");
// 添加用户
this.sysUserMapper.insert(sysUser);
// 抛出异常事务进行回滚
int i = 10 / 0;
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(sysUser.getId());
sysUserRole.setRoleId(1L);
// 添加用户角色
this.sysUserRoleMapper.insert(sysUserRole);
// 事务提交
this.dataSourceTransactionManager.commit(transactionStatus);
} catch (Exception e) {
log.error("添加用户时出现未知异常", e);
// 事务回滚
this.dataSourceTransactionManager.rollback(transactionStatus);
}
}
如果不加 int i = 10 / 0;除零异常
我们可以看到程序正常执行
数据库中的数据也是正常的
一旦我们加了 int i = 10 / 0;除零异常
程序出现异常
数据库中也不会插入数据
这就表示我们的编程式事务没有问题
在这里强调一下 一定在所有可能出现程序结束的地方加 事务的提交或回滚
在这里强调一下 一定在所有可能出现程序结束的地方加 事务的提交或回滚
在这里强调一下 一定在所有可能出现程序结束的地方加 事务的提交或回滚
重要的事情说三遍(因为本人 也因为事务没有回滚导致数据库死锁 的线上问题┭┮﹏┭┮)
一旦使用一定要谨慎
TransactionTemplate使用比DataSourceTransactionManager更加简单
上代码
@Resource
private SysUserMapper sysUserMapper;
@Resource
private SysUserRoleMapper sysUserRoleMapper;
@Resource
private TransactionTemplate transactionTemplate;
/**
* 测试编程式事务
*
* @throws Exception 异常
*/
@Test
public void testTransaction2() throws Exception {
Boolean flag = this.transactionTemplate.execute(status -> {
// 业务代码块
SysUser sysUser = new SysUser();
sysUser.setUsername(RandomStringUtils.randomAlphanumeric(6));
sysUser.setNickname("22223");
sysUser.setPassword("33333");
sysUser.setSalt("123123");
// 添加用户
this.sysUserMapper.insert(sysUser);
// int i = 10 / 0;
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(sysUser.getId());
sysUserRole.setRoleId(1L);
// 添加用户角色
this.sysUserRoleMapper.insert(sysUserRole);
return Boolean.TRUE;
});
if (BooleanUtils.isNotTrue(flag)) {
System.out.println("事务执行失败");
}
}
如果不加 int i = 10 / 0;除零异常
我们可以看到程序正常执行
数据库中的数据也是正常的
一旦我们加了 int i = 10 / 0;除零异常
程序出现异常
数据库中也不会插入数据
这就表示我们的编程式事务没有问题(同上我就不贴图了)
但是我们没有手动提交也没有手动回滚 他是怎么实现的呢
看源码:
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
// 着这里执行了我们的业务代码
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// 出现异常回滚
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) {
// 出现异常回滚
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
// 没有异常提交
this.transactionManager.commit(status);
return result;
}
}
这就是我们没有手动控制事务 也可以进行事务的提交和回滚
手动控制事务回滚
/**
* 测试编程式事务
*
* @throws Exception 异常
*/
@Test
public void testTransaction2() throws Exception {
Boolean flag = this.transactionTemplate.execute(status -> {
SysUser sysUser = new SysUser();
sysUser.setUsername(RandomStringUtils.randomAlphanumeric(6));
sysUser.setNickname("22223");
sysUser.setPassword("33333");
sysUser.setSalt("123123");
// 添加用户
this.sysUserMapper.insert(sysUser);
// 特殊的业务手动回滚
int i = RandomUtils.nextInt();
if (i % 2 == 0) {
status.setRollbackOnly();
return Boolean.FALSE;
}
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(sysUser.getId());
sysUserRole.setRoleId(1L);
// 添加用户角色
this.sysUserRoleMapper.insert(sysUserRole);
return Boolean.TRUE;
});
if (BooleanUtils.isNotTrue(flag)) {
System.out.println("事务执行失败");
}
}
通过上下对比我们明显看出TransactionTemplate比DataSourceTransactionManager更加简单,而且不容易出错,所以在工作中个人还是比较推荐TransactionTemplate的.
注意:
无论是声明式事务还是编程式事务 再开一个线程 事务都不共享
无论是声明式事务还是编程式事务 再开一个线程 事务都不共享
无论是声明式事务还是编程式事务 再开一个线程 事务都不共享