Spring事务的本质其实就是数据库对事务的支持,事务的出现是为了确保数据的完整性和一致性,使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交。
如果不使用Spring,直接使用Connection控制事务,我们需要这么做:
Connection conn = DriverManager.getConnection();
try {
conn.setAutoCommit(false); //将自动提交设置为false
执行CRUD操作
conn.commit(); //当两个操作成功后手动提交
} catch (Exception e) {
conn.rollback(); //一旦其中一个操作出错都将回滚,所有操作都不成功
e.printStackTrace();
} finally {
conn.colse();
}
事务有四大特性,简称为ACID
1.原子性(Atomicity)事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
2.一致性(Consistency)事务在完成时,必须是所有的数据都保持一致状态。
3.隔离性(Isolation)并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性。
4.持久性(Durability)一旦事务完成,数据库的改变必须是持久化的。
1、事务的隔离级别
1.1、事务的隔离级别主要是为了应对事务并发引起的四种问题:
1.脏读:一个事务正在对数据进行更新操作,但是更新还未提交,另一个事务这时也来操作这组数据,并且读取了前一个事务还未提交的数据,而前一个事务如果操作失败进行了回滚,后一个事务读取的就是错误数据,这样就造成了脏读(一个事务读到另一个事务未提交的更新数据)。
2.不可重复读:一个事务多次读取同一数据,在该事务还未结束时,另一个事务也对该数据进行了操作,而且在第一个事务两次次读取之间,第二个事务对数据进行了更新,那么第一个事务前后两次读取到的数据是不同的,这样就造成了不可重复读(一个事务两次读同一行数据,可是这两次读到的数据不一样)。
3.幻读:第一个数据正在查询符合某一条件的数据,这时,另一个事务又插入了一条符合条件的数据,第一个事务在第二次查询符合同一条件的数据时,发现多了一条前一次查询时没有的数据(一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行)。
4.丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。
不可重复读和幻读的区别:
不可重复读是指同一查询在同一事务中多次进行,由于其他提交的事务对数据进行了修改或删除,导致每次返回不同的结果,
幻读是指同一查询在同一事务中多次进行,由于其他提交的事务对数据进行了插入操作,导致每次返回不同的结果集
区别就在于不可重复读能看见其他事务提交的修改和删除,而幻像能看见其他事务提交的插入
1.2、事务的隔离级别(Isolation Level)
隔离级别越高,意味着数据库事务并发执行性能越差,能处理的操作就越少。
Spring事务Isolation有五种属性
1、DEFAULT(默认)
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别
2、Read Uncommitted(读取未提交)
这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
3、Read committed(读取已提交)
这个是Oracle、Sql Server默认的隔离级别,保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。不会出现脏读,会出现不可重复读和幻读。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
4、REPEATABLE_READ(可重复读)
这个是Mysql的默认隔离级别,它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了可重复读,这中隔离级别可以防止出现脏读和不可以重复读,但是会出现幻读。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
5、Serializable(串行化)
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
2、事务的传播行为
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务上下文的规则(是否要有独立的事务隔离级别和锁)
1、@Transactional(propagation=Propagation.REQUIRED) (默认)
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中
2、@Transactional(propagation = Propagation.SUPPORTS)
支持当前事务,如果当前没有事务,就以非事务方式执行
3、@Transactional(propagation = Propagation.MANDATORY)
使用当前的事务,如果当前没有事务,就抛出异常
4、@Transactional(propagation = Propagation.REQUIRES_NEW)
新建一个新的事务,如果原来存在事务,就把原来的挂起,新的执行完毕,再继续执行老的事务
5、@Transactional(propagation = Propagation.NOT_SUPPORTED)
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
6、@Transactional(propagation = Propagation.NEVER)
以非事务方式执行,如果当前存在事务,则抛出异常。(与Propagation.MANDATORY相反)
7、@Transactional(propagation = Propagation.NESTED)
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。