1.声明式事务
@Transactional是 org.springframework.transaction.annotation 包下的一个注解,通常写代码需要保持事务一致性的时候使用。咱们可能以为在方法上加上 @Transactional注解就完事儿了,但产生的结果可能不符合使用预期。事实上还有三个点需要注意:回滚,事务的传播以及加了@Transactional不生效的情况
1.回滚
//异常
Exception:
IOException
SQLException
自定义异常
RuntimeException:
NullPointException
//错误
Error
@Transactional 默认出现RuntimeException和Error进行回滚
//启动测试类对下边的case进行测试
@Test
public void throwRuntimeException(){
testTransactional.throwException();
}
/**
* case1:数据插入失败
*/
@Transactional
public void throwException(){
userMapper.insert(new UserDo(1L,"小明", 15));
throw new RuntimeException();
}
/**
* case2:数据成功插入
*/
@Transactional
public void throwException() throws IOException {
userMapper.insert(new UserDo(1L,"小明", 15));
throw new IOException();
}
/**
* case3:数据插入失败
*/
@Transactional(rollbackFor = Exception.class)
public void throwException() throws IOException {
userMapper.insert(new UserDo(1L,"小明", 15));
throw new IOException();
}
2.事务传播机制
Propagation.REQUIRED:当前有事务就加入,否则创建一个事务运行
Propagation.REQUIRES_NEW:当前有事务就挂起,并创建一个新事务运行。否则创建一个新事务运行
Propagation.SUPPORTS:当前有事务就加入,否则以非事务方式运行
Propagation.NOT_SUPPORTED:以非事务方式运行,当前有事务就挂起
Propagation.NESTED:有事务就在嵌套事务中运行,否则创建一个新事务
Propagation.MANDATORY:有事务就加入,否则抛出异常
Propagation.NEVER:以非事务方式运行,有事务则抛出异常
运行测试方法
@Test
public void throwRequired() {
testOuterTransactionalServiceImpl.outer();
}
case中的方法所在类的结构
@Service
public class TestOuterTransactionalServiceImpl {
@Autowired
private TestInnerTransactionalServiceImpl testSubTransactional;
@Autowired
private UserMapper userMapper;
@Autowired
private ApplicationContext applicationContext;
@Transactional()
public void outer(){
//case中的方法内容
}
}
@Service
public class TestInnerTransactionalServiceImpl {
@Autowired
private UserMapper userMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() throws Exception {
//case中的方法内容
}
}
默认的事务传播行为:Propagation.REQUIRED:当前有事务就加入,否则创建一个事务运行
//case1:数据都不会被插入,都在同一个事务中,一个方法抛异常都会回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
//case2:数据都不会被插入,都在同一个事务中,一个方法抛异常都会回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
throw new RuntimeException();
}
@Transactional(rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
//case3:数据小明插入成功,数据小红插入失败。小红创建了一个新的事务,抛出异常回滚
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
Propagation.REQUIRES_NEW:当前有事务就挂起,并创建一个新事务运行。否则创建一个新事务运行
//case1:数据小红插入成功,数据小明插入失败。小红创建了一个新的事务,外部异常回滚和她无关
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
//case2:数据小明插入成功,数据小红插入失败。outer()无事务。inner()创建了一个新的事务,出现异常回滚
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
Propagation.SUPPORTS:当前有事务就加入,否则以非事务方式运行
//case1:数据都不会被插入,都在同一个事务中,一个方法抛异常都会回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
//case2:数据都不会被插入,都在同一个事务中,一个方法抛异常都会回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
//case3:数据都插入成功,当没有事务时,inner()会以非事务方式运行
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
//throw new RuntimeException();
}
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
Propagation.NOT_SUPPORTED:以非事务方式运行,当前有事务就挂起
//case1:数据小红插入成功,数据小明插入失败。inner()会将原来的事务挂起,以非事务的方式运行
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
//验证俩个方法不在同一个事务之中
throw new RuntimeException();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
//验证以非事务的方式运行
throw new RuntimeException();
}
Propagation.MANDATORY:有事务就加入,否则抛出异常
//case1:数据都不会被插入,都在同一个事务中,一个方法抛异常都会回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
//case2:数据都不会被插入,都在同一个事务中,一个方法抛异常都会回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
//case3:数据小明插入成功,数据小红插入失败,抛出异常
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
Propagation.NESTED:有事务就在嵌套事务中运行,否则创建一个新事务
//case1:数据都不会被插入,outer()方法抛异常,导致两个方法都回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
throw new RuntimeException();
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
//case2:数据都不会被插入,inner()方法抛异常,导致两个方法都回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
//case3:数据小明插入成功,数据小红插入失败。outer()没有事务,inner()会创建一个新事务执行
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
Propagation.NEVER:以非事务方式运行,有事务则抛出异常
//case1:数据都不会被插入,outer()存在事务,导致inner()抛出异常,两个方法都回滚
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
//case2:数据都会被插入,outer()不存在事务,inner()以非事务方式运行
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
3.不生效的情况
1.多线程
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
new Thread(new Runnable() {
@Override
public void run() {
testSubTransactional.inner();
}
}).start();
//throw new RuntimeException();
}
@Transactional(rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
俩个方法之间不会相互影响,一个抛出异常不会影响另一个回滚
2.类内调用未加注解的方法A调用加注解的方法B
//数据全部插入成功,因为@Transactional是基于aop做增强在方法前后加上事务相关的处理,调用内部方法没有从容器中获取增强方法,导致失效
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
this.inner();
}
@Transactional(rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
//修改方法1 将两个方法放到不同的类中
//修改方法2
第一步:注入applicationContext
@Autowired
private ApplicationContext applicationContext;
第二步:通过applicationContext获取bean
TestOuterTransactionalServiceImpl testOuterTransactionalServiceImpl = (TestOuterTransactionalServiceImpl) applicationContext.getBean("testOuterTransactionalServiceImpl");
testOuterTransactionalServiceImpl.inner();
3.修饰符
//使用private修饰被@Transactional注解的方法
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
TestOuterTransactionalServiceImpl testOuterTransactionalServiceImpl = (TestOuterTransactionalServiceImpl) applicationContext.getBean("testOuterTransactionalServiceImpl");
testOuterTransactionalServiceImpl.inner();
}
@Transactional(rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
}
4.try-catch捕获异常
//case1:数据小明插入成功,数据小红插入失败。inner()的异常被outer()捕获了
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
try {
testSubTransactional.inner();
} catch (Exception e) {
System.out.println(e);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
//case2:数据都插入成功。异常都被捕获了
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
try {
testSubTransactional.inner();
} catch (Exception e) {
System.out.println(e);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
try {
throw new RuntimeException();
} catch (RuntimeException e) {
System.out.println(e);
}
}
5.抛出异常不符
//数据小明插入成功,数据小红插入失败。inner()抛出的异常无法被outer()识别
@Transactional()
public void outer() throws Exception {
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void inner() throws Exception {
userMapper.insert(new UserDo(1L,"小红", 15));
throw new Exception();
}
//所以要@Transactional(rollbackFor = Exception.class)
6.夹杂dml语句中夹杂ddl
在同一个事务中,ddl执行的时候会将之前的事务进行commit,这导致你回滚的时候,只能回滚ddl语句之后的操作
4.使用@Transactional 注解出现异常 “Transaction rolled back because it has been marked as rollback-only”
解决方法:
1.外层不捕捉
2.用创建新事务的传播机制Propagation.REQUIRES_NEW 或者 Propagation.NESTED
//case1:抛出以上异常,仍然会回滚代码。数据都不会插入
//处于同一个事务中的两个方法,外层try_catch 内层抛出异常
@Transactional(rollbackFor = Exception.class)
public void outer(){
userMapper.insert(new UserDo(1L,"小明", 15));
try {
testSubTransactional.inner();
} catch (Exception e) {
System.out.println(e);
}
}
@Transactional(rollbackFor = Exception.class)
public void inner(){
userMapper.insert(new UserDo(1L,"小红", 15));
throw new RuntimeException();
}
2.编程式事务
PlatformTransactionManager 事务的操作方法
TransactionDefinition 事务的传播行为,隔离级别等
TransactionStatus 判断事务完成或是回滚
TransactionTemplate 实现编程式事务
DefaultTransactionDefinition 是TransactionDefinition的实现类
TransactionTemplate 主要使用上述三个类完成事务操作
public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean {
@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);
Object result;
try {
result = action.doInTransaction(status);
} catch (Error | RuntimeException var5) {
this.rollbackOnException(status, var5);
throw var5;
} catch (Throwable var6) {
this.rollbackOnException(status, var6);
throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
}
public void outer() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
//doInTransactionWithoutResult没有返回值
protected void doInTransactionWithoutResult(TransactionStatus status) {
userMapper.insert(new UserDo(1L,"小明", 15));
testSubTransactional.inner();
}
});
}
public void inner() {
transactionTemplate.setPropagationBehavior(3); //设置传播行为,从TransactionDefinition找值
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
//doInTransaction有返回值
public Object doInTransaction(TransactionStatus status) {
userMapper.insert(new UserDo(1L,"小红", 15));
return 1;
}
});
}