事务有四大特性,分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),把这四大特性的英文首字母拼起来,就是ACID,这个单词对应的中文意思是“酸”,什么东西比较酸呢,柠檬(🍋),把它们联系起来,就容易记着这几个特性了。
在MySQL数据库中,默认的存储引擎Innodb是支持事务的,另一个比较常用的存储引擎MyISAM是不支持事务的。
插曲说一下,一直对MyISAM怎么读比较纠结,我查了一下,在mysql的邮件列表,有人问过这个问题,提到比较多的读法是 [My-I-sam],有相同困惑的朋友可以借鉴一下。
这四个特性的具体含义,这里就不再细说了,我们焦点放在在实际的编程中怎么避免踩坑。
声明式事务的属性
spring对事务的支持有两种方式:编程式事务和声明式事务。编程式事务对代码有侵入性,用起来也比较繁琐,一般也很少使用,声明式事务通过添加 @Transactional 注解来支持,事务的属性会添加在注解上。
声明式事务的几个属性需要了解。
事务传播级别
- TransactionDefinition.PROPAGATION_REQUIRED
@Transactional 注解默认的传播方式就是REQUIRED,在该传播模式下,如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_SUPPORTS
表示如果有事务,就加入到当前事务,如果没有,那也不开启事务执行。这种传播级别可用于查询方法,因为SELECT语句既可以在事务内执行,也可以不需要事务;
- TransactionDefinition.PROPAGATION_MANDATORY
表示必须要存在当前事务并加入执行,否则将抛出异常。这种传播级别可用于核心更新逻辑,比如用户余额变更,它总是被其他事务方法调用,不能直接由非事务方法调用;
- TransactionDefinition.PROPAGATION_REQUIRES_NEW
表示不管当前有没有事务,都必须开启一个新的事务执行。如果当前已经有事务,那么当前事务会挂起,等新事务完成后,再恢复执行;
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED
表示不支持事务,如果当前有事务,那么当前事务会挂起,等这个方法执行完成后,再恢复执行;
- TransactionDefinition.PROPAGATION_NEVER
不支持事务,而且在检测到当前有事务时,会抛出异常拒绝执行;
- TransactionDefinition.PROPAGATION_NESTED
表示如果当前有事务,则开启一个嵌套级别事务,如果当前没有事务,则开启一个新事务。
事务隔离级别
在 org.springframework.transaction.annotation.Isolation 枚举中定义了@Transactional支持的隔离级别:
- DEFAULT 使用数据库指定的隔离级别
- READ_UNCOMMITTED 读未提交
- READ_COMMITTED 读已提交
- REPEATABLE_READ 可重复读
- SERIALIZABLE 可串行化
超时属性
一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为-1。-1代表使用底层事务系统默认的超时时间。
只读属性
对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。只读事务不涉及数据的修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。
可以通过下面的语句查询数据库是否开启了自动提交,如果开启了,默认一条语句就是一个事务。