运行流程
@Transactional源码
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
- timeout:事务可以允许的时间戳,单位是秒
- readOnly:在只读事务执行更新操作会发生异常
rollbackForClassName() noRollbackFor() noRollbackForClassName)
定义什么异常情况下提交事务,什么异常的情况下回滚事务。
Spring事务管理器
隔离级别isolation
前言 - 两类丢失更新
- 第一类丢失更新:由于回滚引发
目前大部分数据库已经克服了第一类丢失更新的问题,也就是现今数据库系统已经不会再出现上述的情况了,不再讨论它。
- 第二类丢失更新:由于提交引发
为了克服上述两类问题,数据库提出了事务之间隔离级别的概念。
未提交读
未提交读( read uncommitt )是最低的隔离级别,其含义是允许一个事务读取另外一个事务没有提交的数据。未提交读是一种危险的隔离级别,所以一般在我们实际的开发 应用不广 但是它的优点在于并发能力高,适合那些对数据一致性没有要求而追求高并发的场景。它会产生脏读
读写提交
读写提交( read committed )隔离级别, 是指一个事务只能读取另外一个事务已经提交的数据。它可以克服脏读
但是读写提交会导致不可重复读
可重复读
可重复读的目标是克服读写提交中出现的不可重复读的现象,因为在读写提交的时候,可能出些值的变化, 影响当前事务的执行。它能够克服不可重复读的现象
但是它会产生幻读
幻读针对多条记录,而可重复读是针对单条记录
串行化
串行化(Serializable)是数据库最高的隔离级别,它会要求所有的 SQL 都会按照顺序执行,这样就可以克服上述隔离级别出现的各种问题,所以它能够完全保证数据的一致性
总结
传播行为
在Spring中,当一个方法调用另外一个方法时,可以让事务采取不同的策略工作,如新建事务或者挂起当前事务等,这便是事务的传播行为。
传播行为枚举源码
public enum Propagation {
/**
* 需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务
* 去否则新建一个事务运行子方法
*/
REQUIRED(0),
/**
* 支持事务,如果当前存在事务,就沿用当前事务
* 如果不存在,则继续采用无事务的方式运行子方法
*/
SUPPORTS(1),
/**
* 必须使用事务,如果当前没有事务,则会抛出异常,
* 如果存在当前事务 就沿用当前事务
*/
MANDATORY(2),
/**
* 无论当前事务是否存在,都会创建新事务运行方法
* 这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立
*/
REQUIRES_NEW(3),
/**
* 不支持事务,当前存在事务时,将挂起事务,运行方法
*/
NOT_SUPPORTED(4),
/**
* 不支持事务,如果当前方法存在事务,则抛出异常,否则继续使用无事务机制运行
*/
NEVER(5),
/**
* 在当前方法调用子方法时,如果子方法发生异常,
* 只因滚子方法执行过的 SQL ,而不回滚当前方法的事务
*/
NESTED(6);
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
测试
常用的传播行为只有三种:REQUIRED
、REQUIRES_NEW
和NESTED
NESTED
播行为会沿用当前事务的隔离级别和锁等特性,而REQUIRES_NEW
则可以拥有自己独立的隔离级别和锁等特性。
@Transactional 自调用失效问题
直接调用本类方法会导致传播行为失效
在自调用的过程中是类自身的调用 ,而不是代理对象去调用, 那么就不会产生 AOP,这样 Spring就不能把你的代码织入到约定的流程中 于是就产生了现在看到的失败场景。 为了克服这个问题,用一个 Service 去调用另一个Service。
备注
默认的隔离级别
Oracle 默认的隔离级别为读写提交, MySQL 则是可重复读。
ACID
- Atomic (原子性) 事务中包含的操作被看作一个整体的 务单元这个业务单元中的操作要么全部成功,要么全部失败,不会出现部分失败、部分成功的场景。
- Consistency (一致性):事务在完成时,必须使所有的数据都保持一致状态,在数据库中所有的修改都基于事务,保证了数据的完整性。
- Isolation (隔离性) 这是我们讨论的核心内容,正如上述,可能多个应用程序线程同时访问数据,这样数据库同样的数据就会在各个不同的事务中被访问,这样会产生丢失更新。为了压制丢失更新的产生,数据库定义了隔离级别的概念,通过它的选择,可以在不同程度上压制丢失更新的发生。因为互联网的应用常常面对高并发的场景,所以隔离性是需要掌握的重点内容。
- Durability (持久性):事务结束后,所有的数据会固化到一个地方,如保存到磁盘当中,即使断电重启后也可以提供给应用程序访问。