关于事务一直有个误解,数据库的事务和Spring(语言)的事务有区别和联系吗,一直会有搞混。下面以Mysql数据库和Spring来做对比说明,梳理清楚。
Mysql 事务
隔离级别 | 值 | 名称 | 问题描述 |
---|---|---|---|
Read Uncommitted | 0 | 脏读 | 1:事务A,增、删、改,未提交;2:事务B,读取未提交的数据;3:事务A回滚;4:事务B看到的数据就是脏数据 |
Read committed | 1 | 不可重复读 | 1:事务A,两次读操作;2:事务B,在事务A的两次读之间进行了修改;3:事务A第二次读的数据不一致 |
Repeatable Read | 2 | 幻读 | 1:事务A做范围数据的批量修改,比如:age > 10 set class='small';2:事务B在 age > 10的范围内,只更新了一条数据,比如 age = 11 and id=111 set class='very small';3:事务A对事务B修改的数据丢失 |
Serializable | 3 | 串行化 | 事务只能一个一个执行,最高等级,速度慢,避免了所有问题 |
Spring 声明式注解事务
使用声明式注解事务是比较简单的方式,将事务管理器交予 Spring 管理,在目标类或者目标方法上添加注解 @Transactional 即可
类型 | 名称 | 描述 |
---|---|---|
事务传播行为 Propagation | REQUIRED | 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务;默认选择 |
SUPPORTS | 当前如果有事务,Spring就会使用该事务;否则不会开始一个新事务 | |
MANDATORY | 当前如果有事务,Spring就会使用该事务;否则会抛出异常 | |
REQUIRES_NEW | Spring总是开始一个新事务。如果当前有事务,则该事务挂起 | |
NOT_SUPPORTED | Spring不会执行事务中的代码。代码总是在非事务环境下执行,如果当前有事务,则该事务挂起 | |
NEVER | 即使当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则抛出异常 | |
NESTED | 如果当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与Transaction- Definition.PROPAGATION_REQUIRED一样 | |
事务的隔离级别 Isolation | DEFAULT | 默认隔离级别(对大多数数据库来说就是 READ_COMMITTED) |
READ_UNCOMMITTED | 对应数据库 Read Uncommitted;产生 脏读、不可重复读、幻读 | |
READ_COMMITTED | 对应数据库 Read Committed;避免 脏读,产生不可重复读、幻读 | |
REPEATABLE_READ | 对应数据库 Repeatable Read;避免 脏读、不可重复读,产生 幻读 | |
SERIALIZABLE | 对应数据库 Serializable,隔离级别最高 |
@Transactional 注解配置事务的参数
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 {}; }
一般我们使用 Spring 事务时,很少去修改事务的参数,直接使用默认值就行,导致对其配置参数不甚了解。上面通过对比 Spring 和 数据库的 事务描述,能理清楚两者之间的关系,清晰两者的定义,理解后再做到不混淆。