@Transactional失效的场景及解决
1.@Transactional修饰的方法为非public方法,这个时候@Transactional会实现。
失败的原理是:@Transactional是基于动态代理来实现的,非public的方法,他@Transactional的动态代理对象信息为空,所以不能回滚。
2.在类内部没有添加@Transactional的方法,调用了@Transactional方法时,当你调用是,他也不会回滚
测试代码如下:
@Service public class UserServiceImpl extends implements UserService { @Autowired private UserMapper userMapper; @Override @Transactional public void insertOne() { User user = new User(); user.setUsername("123"); //插入到数据库 userMapper.insert(user); //手动抛出异常 throw new IndexOutOfBoundsException(); } }
失败的原理:@Transactional是基于动态代理对象来实现的,而在类内部的方法的调用是通过this关键字来实现的,没有经过动态代理对象,所以事务回滚失效。
Spring框架的事务处理代码默认地只在抛出运行时和unchecked exceptions时才标识事务回滚。 也就是说,当抛出个RuntimeException 或其子类的实例时,(Errors 也一样 - 默认地 - 标识事务回滚。)
从事务方法中抛出的Checked exceptions将不被标识进行事务回滚。
解决办法:
在@Transactional中加上(rollbackFor = Exception.class) ==》@Transactional(rollbackFor = Exception.class)
3.就是在@Transactional方法内部捕获了异常,没有在catch代码块里面重新抛出异常,事务也不会回滚。
测试代码如下:
@Override @Transactional public void insertOne() { try { User user = new User(); user.setUsername("123"); //插入到数据库 userMapper.insert(user); //手动抛出异常 throw new IndexOutOfBoundsException(); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } }
解决办法@Transactional的方法里面捕获了异常,手动回滚,
代码如下:
@Override @Transactional public void insertOne() { try { User user = new User(); user.setUsername("123"); //插入到数据库 userMapper.insert(userEntity); //手动抛出异常 throw new IndexOutOfBoundsException(); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }
4.创建表时,引擎是否是InnerDB
mysql数据库常用的引擎有两种(innodb和myisam),在建表时有些dba会默认使用myisam引擎,如果是这种引擎,那么在遇到异常时,数据库是不回滚的。所以将需要回滚的表引擎改为innodb
解决方法:
ALTER TABLE tableName CHANGE TYPE=InnoDB;