目录
1.什么是Spring 事务
事务在逻辑上是一组操作,要么执行,要不都不执行。
如下:
Begin;
insert into student values(1,"test",22,"male");
select * from student;
commit;
上述SQL语句,一般情况是一句一句提交并执行;而开启事务后,上述语句会一起提交并执行。
事务操作本来应该由数据库进行控制,但为了方便用户进行业务逻辑的拓展,spring对事务功能进行了拓展实现。
所以,Spring 事务其实是数据库事务的拓展而已。其根本上,还是要连接数据库,并开启数据库事务进行执行或者回滚等操作。
2.Spring 事务的开启方式
Spring支持两种事务方式,分别是编程式事务(用户通过代码来控制事务的处理逻辑)和声明式事务(通过@Transactional注解实现)。一般我们开发中使用声明式事务较多。
// 默认是RuntimeException就回滚,传播机制为REQUIRED
@Transactional
public boolean transactionTest(UserAccount user) {
try {
//业务执行逻辑
//userDao.insert(user);
int i =0; //模拟业务出错
i = 100 /i;
}catch (Exception e){
log.error("被除数为0");
//事务回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}
return true;
}
//数据源
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
//事务
@Autowired
TransactionDefinition transactionDefinition;
public Boolean execute(){
// 手动开启事务
TransactionStatus transactionStatus = null;
try {
//获取事务对象
transactionStatus =dataSourceTransactionManager.getTransaction(transactionDefinition);
// 具体代码逻辑
// 提交事务
dataSourceTransactionManager.commit(transactionStatus);
} catch (Exception e) {
log.error("xxxxxx",e);
if (ts != null) {
dataSourceTransactionManager.rollback(ts);
}
throw new RollBackException("业务出错了!");
}
return true;
}
上述代码皆为声明式事务:代码段1中使用@Transactional注解开启,较为便捷;代码段2则是手动开启事务,通过TransactionDefinition对象来开启并提交事务。
3.Spring事务的实现方式/原理
Spring事务是是由AOP来实现的,主要通过TransactionInterceptor类,调用invoke()方法实现具体逻辑。
首先,当一个方法添加@Transactional后,spring会基于这个类生成一个代理对象。
当使用这个代理对象的方法时,解析方法上事务相关的属性,判断是否开启事务。若开启,则把关闭自动提交,开启事务执行具体的业务逻辑。
(invokeWithinTransaction方法中,获取事务属性相关代码,主要判断事务的类型)
如果执行逻辑没有出现异常,那么代理逻辑会通过cmomitTransactionAfterReturning()完成事务的提交,提交的具体逻辑则是由doCommit()实现;
如果出现异常,那么会通过completeTransactionAfterThrowing()进行回滚操作,具体逻辑由doRollBack()实现。同时,用户也可以控制对哪些异常进行回滚操作。
事务执行完毕,会调用cleanupTransactioninfo()清除相关的事务信息。
(invokeWithinTransaction方法中,声明式事务的具体执行逻辑)
4.事务传播机制
事物的传播机制是指不同方法的嵌套调用过程中,事务如何进行处理,事务之间的相互处理关系。比如说,A类中的a方法要调用B类中的b方法,是用同一个事务还是两个事务;出现异常是回滚还是提交。
具体分为7种:以做作业为例进行解释
- REQUIRED:默认传播特性,如果当前没有事务,则新建事务;如果当前存在事务,则加入事务。(你做作业,我就抄一下;你没做作业,我就自己做)
- SUPPORTS:当前存在事务,则加入事务;当前没有事务,则以非事务方式执行。(你做作业,我就抄一下,你没做作业,我也不做)
- MANDATORY:当前存在事务,则加入事务;当前没有事务,抛出异常。(你做作业,我就抄一下,你没做作业,警告你)
- REQUIRED_NEW:创建一个新事物,如果当前存在事务,则挂起事务。(不过你有没有做作业,我都自己做)
- NOT_SUPPORTED: 以非事务方式执行, 如果当前存在事务,则挂起事务。(大家都不做作业,如果你做了,我把你作业撕掉)
- NEVER:不使用事务,如果当前事务存在,抛出异常。
- NESTED:如果当前事务存在,则在嵌套事务(设立保存点,保存父事务的状态,出现异常时只有子事务会回滚)中执行,否则与REQUIRED一致。
一般开发中,使用 required、required_new、nested较多。
5.事务隔离级别
Spring 事务中的事务隔离级与数据库的数据隔离级别是一致的,从低到高,有以下四类:
Read uncommitted:读未提交;当前隔离级别下,所有事务都可以看到未提交事务的执行结果(SQL语句的结果)。会导致脏读问题,所以一般情况不会采用。脏读:一个事务读取了未提交事务的数据,而该数据并非是最终持久化的数据(即读取的不一定是最终应该读取的正确数据)
read committed读已提交;一个事务开始时只能看见已经提交事务所做的改变,事务提交前的所有数据变化都是不可见的。(未commit不可见)。会产生不可重复读问题。不可重复读:对于数据库中的某行数据,一个事务执行过程中多次查询,但由于其他事务进行了修改,导致返回的是不同的查询结果。
repeatable read可重复读;可重复读,MySQL默认。保证了同一个事务的多个实例并发读取事务时看到的是相同的数据行,解决了不可重复读问题,但会产生幻读问题(MVCC可解决)。幻读:例如事务t1对表中某列值为1的数据进行批量修改,同时,事务t2在表中插入了一条那一列值为1的数据,并完成提交。此时,事务t1查看刚执行完成的数据时会发现还有一条数据没有进行修改
serializable:串行化。最高隔离级别,通过强制事务排序,使之不可能相互冲突。
低级别的隔离一般支持更高的并发处理,并且拥有更低的系统开销。隔离级别越高,则数据越安全,但开销更大。
值得一提的是:如果数据库与spring隔离级别不一致,以spring配置为主。
6.事务失效的原因
一些情况下会导致事务失效,可能的原因如下;
- 1)bean对象没有被spring容器管理。
- 2)方法的访问修饰符不是public。
- 3)自身调用问题。
- 4)数据源没有配置事务管理器。
- 5)数据库不支持事务。
- 6)异常被捕捉。
- 7)异常类型错误或者配置错误。