事务在平常开发会经常遇到,介于有些同学对事务的理解还不是很透彻,在此稍作总结下
1.什么是事务
把一堆事情绑在一起做,都成功了才算完成,否则就恢复之前的样子。
2.事务的特性ACID(核心为C,即一致性,其他三个均是为一致性服务的)
- 原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做 。
- 一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。比如,当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统在运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数 据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。
- 隔离性:一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持续性:指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
3.spring中事务
- Spring事务管理高层抽象主要包括3个接口
- PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
- TransactionDefinition: 事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理
- TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息
3.1Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现类
事务 | 说明 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManager | 使用Spring JDBC或iBatis 进行持久化数据时使用(咱项目是ORM是myBatis,所以用这) |
org.springframework.orm.hibernate3.HibernateTransactionManager | 使用Hibernate3.0版本进行持久化数据时使用 |
org.springframework.orm.jpa.JpaTransactionManager | 使用JPA进行持久化时使用 |
org.springframework.jdo.JdoTransactionManager | 当持久化机制是Jdo时使用 |
org.springframework.transaction.jta.JtaTransactionManager | 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 |
- DataSourceTransactionManager针对JdbcTemplate、MyBatis 事务控制 ,使用Connection(连接)进行事务控制 :
- 开启事务 connection.setAutoCommit(false);
- 提交事务 connection.commit();
- 回滚事务 connection.rollback();
- HibernateTransactionManager针对Hibernate框架进行事务管理, 使用Session的Transaction相关操作进行事务控制 :
- 开启事务 session.beginTransaction();
- 提交事务 session.getTransaction().commit();
- 回滚事务 session.getTransaction().rollback();
- TransactionDefinition事务定义信息,该接口主要提供的方法:
- lgetIsolationLevel:获取隔离级别
- lgetPropagationBehavior:获取传播行为
- lgetTimeout:获取超时时间(事务的有效期)
- lisReadOnly 是否只读(保存、更新、删除—对数据进行操作-变成可读写的,查询-设置这个属性为true,只能读不能写),事务管理器能够根据这个返回值进行优化。
这些事务的定义信息,都可以在配置文件中配置和定制。
4.事务的隔离级别
隔离级别 | 说明 |
---|---|
DEFAULT | 使用后端数据库默认的隔离级别(spring中的默认选择项,mysql为可重复度) |
READ_UNCOMMITED | 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 |
READ_COMMITTED | 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生 |
REPEATABLE_READ | 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。 |
SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。 |
5.事务不回滚原因
- 默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚;
- spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚 !
解决方案 - 如果是service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
- 在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常