Java事务
1.事务及其定义
事务是一种机制,即将一个业务的所有操作组合成一个不可分割的执行单元,组成事务的所有操作只有在所有操作均能正常执行的情况下方能提交,只要其任一操作执行失败,都将导致整个事务的回滚。
简单地说,事务是一种“要么不做,要么全做”的机制。
2.事务的特性
事务特性(ACID)包括原子性、一致性、隔离性、持久性。
-
原子性(Atomicity)
原子性是指事务是一个不可分割的单元,事务的所有操作要么全部成功,要么全部失败。例如,同一事务中的SQL语句,要么全部执行成功,要么全部执行失败。 -
一致性(Consistency)
事务必须达到预期结果,数据状态是预期的状态。 -
隔离性(Isolation)
事务隔离性是指多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其他事务的操作数据打断,多个并发事务之间要相互隔离。 -
持久性(Durability)
持久性是指事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不会产生影响。
3.并发事务问题
- 脏读
所谓脏读,就是指事务A读到了事务B还没有提交的数据。
- 不可重复读
不可重复读指在一个事务里面读取了两次数据,读出来的数据不一致。
- 幻读
所谓幻读指在一个事务里面的操作中发现了未被操作的数据。
3.事务隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(READ_UNCOMMITED) | 是 | 是 | 是 |
读已提交(READ_COMMITED) | 否 | 是 | 是 |
可重复读(REPEATABLE_READ) | 否 | 否 | 是 |
串行化(SERLALIZABLE) | 否 | 否 | 否 |
4.Spring事务
4.1 Spring事务机制
Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式两种。编程式事务指的是通过编码方式实现事务;声明式事务基于AOP,将具体的逻辑与事务处理解耦。实际使用中声明式事务用的比较多。
声明式事务有两种方式,一种是在配置文件(XML)中做相关的事务规则声明,另一种是基于@Transactional 注解的方式。
- 默认配置下 Spring 只会回滚运行时、未检查异常(继承自RuntimeException 的异常)或者 Error。
- @Transactional 注解只能应用于public 方法。
4.2 @Transactional 注解属性
isolation 属性:事务的隔离级别, 默认值为 Isolation.DEFAULT。
- Isolation.DEFAULT: 使用底层数据库默认的隔离级别。
- Isolation.READ_UNCOMMITTED: 读取未提交数据,基本不使用。
- Isolation.READ_COMMITTED: 读取已提交数据(会出现不可重复读和幻读)。
- Isolation.REPEATABLE_READ: 可重复读(会出现幻读)。
- Isolation.SERIALIZABLE: 串行化。
timeout 属性:事务的超时时间, 默认值为 -1。 如果超过该时间限制但事务还没有完成, 则自动回滚事务。
rollbackFor 属性:用于指定能够触发事务回滚的异常类型, 可以指定多个异常类型。
noRollbackFor 属性:抛出指定的异常类型, 不会滚事务, 也可以指定多个异常类型。
readOnly 属性:指定事务是否为只读事务, 默认值为 false; 为了忽略那些不需要事务的方法, 比如读取数据, 可以设置 read-only 为 true。
propagation 属性:事务传播行为, 默认值Propagation.REQUIRED。
- REQUIRED:表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。
- SUPPORTS: 如果存在一个事务,支持当前事务,如果没有事务,则非事务执行。
- MANDATORY:表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。
- REQUIRED_NEW:开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起。
- NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务。
- NEVER:表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
- NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。
4.3 事务传播行为详解
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
// 方法A
@Transactional
public void methodA{
methodB();
}
// 方法B
@Transactional
public void methodB{
}
注意: 事务传播行为是站在methodB的角度上来讲的。
4.3.1 REQUIRED
支持当前事务,如果当前没有事务,就新建一个事务。即不管有几个事务存在,都合并成一个事务来处理,只要有一个事务抛出异常,所有事务都会回滚;
4.3.2 SUPPORTS
如果存在一个事务,支持当前事务,如果没有事务,则非事务执行。
4.3.3 MANDATORY
如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4.3.4 REQUIRES_NEW
开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起。
4.3.5 NOT_SUPPORTED
总是非事务地执行,并挂起任何存在的事务。
4.3.6 NEVER
总是非事务地执行,不加入任何事务;如果存在一个活动事务,则抛出异常。
4.3.7 NESTED
如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按REQUIRED执行。
4.4 事务传播原理
4.5 事务不生效的原因
-
数据库引擎不支持。Innodb引擎支持事务,MyISAM引擎不支持事务。
-
未指定rollbackFor参数。默认只回滚运行时、未检查异常(继承自 RuntimeException 的异常)或 Error。
-
未指定transactionManager参数。默认的transactionManager非期望值,或一个事务涉及多个数据库。
-
AOP拦截失效。截面拦截失效,导致@Transactional注解无效。
-
事务方法或类非public。非public方法或类,导致@transactional注解无效。