Spring 事务 我们在实际业务场景中经常会遇到数据频繁修改读取的问题。在同一时刻不同的业务逻辑对同一个表数据进行修改这种冲突很可能造成数据不可挽回的错乱所以我们需要用事务来对数据进行管理。一、事务的概念事务必须服从ACID原则。ACID指的是原子性atomicity、一致性consistency、隔离性isolation和持久性durability。通俗理解事务其实就是一系列指令的集合。原子性操作这些指令时要么全部执行成功要么全部不执行。只要其中一个指令执行失败所有的指令都执行失败数据进行回滚回到执行指令前的数据状态。一致性事务的执行使数据从一个状态转换为另一个状态但是对于整个数据的完整性保持稳定。隔离性在该事务执行的过程中无论发生的任何数据的改变都应该只存在于该事务之中对外界不存在任何影响。只有在事务确定正确提交之后才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。持久性当事务正确完成后它对于数据的改变是永久性的。二、并发事务导致的问题第一类丢失更新撤销一个事务时把其他事务已提交的更新数据覆盖。脏读一个事务读取到另一个事务未提交的更新数据。幻读一个事务执行两次查询第二次结果集包含第一次中没有或某些行已经被删除的数据造成两次结果不一致只是另一个事务在这两次查询中间插入或删除了数据造成的。不可重复读一个事务两次读取同一行的数据结果得到不同状态的结果中间正好另一个事务更新了该数据两次结果相异不可被信任。第二类丢失更新是不可重复读的特殊情况。如果两个事物都读取同一行然后两个都进行写操作并提交第一个事物所做的改变就会丢失。三、编程式事务管理和声明式事务管理编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理spring推荐使用TransactionTemplate。声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截然后在目标方法开始之前创建或者加入一个事务在执行完目标方法之后根据执行情况提交或者回滚事务。最大的优点就是不需要通过编程的方式管理事务这样就不需要在业务逻辑代码中掺杂事务管理的代码只需在配置文件中做相关的事务规则声明(或通过基于Transactional注解的方式)便可以将事务规则应用到业务逻辑中。缺点是粒度只能作用到方法级别。声明式事务管理常用的方式一种是基于tx和aop名字空间的xml配置文件另一种就是基于Transactional注解。显然基于注解的方式更简单易用更清爽。四、spring事务特性spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口其中TransactionDefinition接口定义以下特性事务隔离级别隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量TransactionDefinition.ISOLATION_DEFAULT这是默认值表示使用底层数据库的默认隔离级别。对大部分数据库而言通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。TransactionDefinition.ISOLATION_READ_UNCOMMITTED该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读不可重复读和幻读因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。TransactionDefinition.ISOLATION_READ_COMMITTED该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读这也是大多数情况下的推荐值。TransactionDefinition.ISOLATION_REPEATABLE_READ该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。TransactionDefinition.ISOLATION_SERIALIZABLE所有的事务依次逐个执行这样事务之间就完全不可能产生干扰也就是说该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。事务传播行为所谓事务的传播行为是指如果在开始当前事务之前一个事务上下文已经存在此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量TransactionDefinition.PROPAGATION_REQUIRED如果当前存在事务则加入该事务如果当前没有事务则创建一个新的事务。这是默认值。TransactionDefinition.PROPAGATION_REQUIRES_NEW创建一个新的事务如果当前存在事务则把当前事务挂起。TransactionDefinition.PROPAGATION_SUPPORTS如果当前存在事务则加入该事务如果当前没有事务则以非事务的方式继续运行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED以非事务方式运行如果当前存在事务则把当前事务挂起。TransactionDefinition.PROPAGATION_NEVER以非事务方式运行如果当前存在事务则抛出异常。TransactionDefinition.PROPAGATION_MANDATORY如果当前存在事务则加入该事务如果当前没有事务则抛出异常。TransactionDefinition.PROPAGATION_NESTED如果当前存在事务则创建一个事务作为当前事务的嵌套事务来运行如果当前没有事务则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。事务超时所谓事务超时就是指一个事务所允许执行的最长时间如果超过该时间限制但事务还没有完成则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间其单位是秒。默认设置为底层事务系统的超时值如果底层数据库事务系统没有设置超时值那么就是none没有超时限制。事务只读属性只读事务用于客户代码只读但不修改数据的情形只读事务用于特定情景下的优化比如使用Hibernate的时候。默认为读写事务。“只读事务”并不是一个强制选项它只是一个“暗示”提示数据库驱动程序和数据库系统这个事务并不包含更改数据的操作那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化比方说不安排相应的数据库锁以减轻事务对数据库的压力毕竟事务也是要消耗数据库的资源的。但是你非要在“只读事务”里面修改数据也并非不可以只不过对于数据一致性的保护不像“读写事务”那样保险而已。因此“只读事务”仅仅是一个性能优化的推荐配置而已并非强制你要这样做不可spring事务回滚规则指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常然后依据规则决定是否回滚抛出异常的事务。默认配置下spring只有在抛出的异常为运行时unchecked异常时才回滚该事务也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚)而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。