数据库管理系统中事务(transaction)的四个特性(分析时根据首字母缩写依次解释):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。(执行单个逻辑功能的一组指令或操作称为事务)
原子性(Atomicity):原子性是指一个事务是不可分隔的工作单位,事务中的操作要么都发生,要么都不发生
一致性(Consistency): 事务必须使数据库从一个一致性状态变换到另外一个一致性状态
隔离性(lsolation): 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
持久性(Durability): 持久性是指一个事务一旦被提交,他对数据库中的数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
数据库提供的四种事务隔离级别
Oracle 支持的2种事务隔离级别: READ COMMITED, SERIALIZABLE.Oracle默认的事务的隔离级别为: READ COMMITED 。
mySql支持 4 种事务隔离级别。mySql默认的事务隔离级别为: REPEATABLE READ。
四种隔离级别主要解决的问题是数据库中的并发问题
对于同时运行的多个事务,当这些事务访问数据库中的相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题
1.脏读: 对于两个事务T1,T2,T1 读取了被T2更新但还没被提交的字段。之后,若T2会滚,T1读取的内容就是临时且无效的
2.不可重复读: 对于两个事务T1,T2, T1 读取了一个字段,然后T2更新了该字段。之后,T1再次读取同一个字段,值就不同了
3.幻读: 对于两个事务的T1,T2,T1从一个表中读取了一个子u但,然后T2在该表中插入了一些新的行。之后,如果T1再次读取同一个表,就会多出几行
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题
一个事务于其他事务隔离的程度成为隔离级别,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,但隔离级别越高,数据一致性就越好,但并发性就越差
在需要事务的类或者方法(service)上面添加@Transactional()
注解,里面可以配置需要的粒度,如开头说到的,如果没有设置默认的事务等级,需要在此添加isolation和propagation属性,还有几个其他的属性可以设置,在此只介绍这两个比较重要的属性
在需要事物的类或方法上使用@Transactional(import org.springframework.transaction.annotation.Transactional)
一、开启事物管理
//import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication @EnableTransactionManagement //显示开启事物管理,springboot默认已经开启事物管理,可不用显示的注解 public class Application extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(Application.class); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
二、配置事物
在需要事物的类或方法上使用@Transactional(import org.springframework.transaction.annotation.Transactional)
1、isolation 隔离级别(Springboot默认值为Isolation.DEFAULT)
用法:
@Transactional(isolation=Isolation.DEFAULT) public void test() { }
枚举值(org.springframework.transaction.annotation):
public enum Isolation { DEFAULT(TransactionDefinition.ISOLATION_DEFAULT), READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED), READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED), REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ), SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); }
DEFAULT
:默认值,表示使用底层数据库的默认隔离级别。大部分数据库为READ_COMMITTED(MySql默认隔离级别为REPEATABLE)
READ_UNCOMMITTED
:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。 READ_COMMITTED
:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。 REPEATABLE_READ
:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。 SERIALIZABLE
:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
通过使用 isolation
属性设置,例如:@Transactional(isolation = Isolation.DEFAULT)/2.
2、事物传播行为 propagation(springboot默认值为Propagation.REQUIRED)
用法
@Transactional(propagation=Propagation.REQUIRED) public void test() { }
枚举值(org.springframework.transaction.annotation.Propagation)
public enum Propagation { REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED), SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS), MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY), REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW), NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED), NEVER(TransactionDefinition.PROPAGATION_NEVER), NESTED(TransactionDefinition.PROPAGATION_NESTED); }
REQUIRED
:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 SUPPORTS
:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 MANDATORY
:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 REQUIRES_NEW
:创建一个新的事务,如果当前存在事务,则把当前事务挂起。 NOT_SUPPORTED
:以非事务方式运行,如果当前存在事务,则把当前事务挂起。 NEVER
:以非事务方式运行,如果当前存在事务,则抛出异常。 NESTED
:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED
。
指定方法:通过使用 propagation
属性设置,例如:@Transactional(propagation = Propagation.REQUIRED)
Spring定义了七种传播行为:
3、readOnly=true只读,不能更新,删除
@Transactional(readOnly=true)
三、常见坑点
1、遇到异常检测不回滚,原因:默认RuntimeException级别才回滚,如果是Eexception级别的异常需要手动添加
@Transactional(rollbackFor=Exception.class)
2、捕捉异常后事物不生效,原因:捕捉处理了异常导致框架无法感知异常,自然就无法回滚了。建议:若非实际业务要求,则在业务层统一抛出异常,然后在控制层统一处理
@Transactional(rollbackFor=Exception.class) public void test() { try { //业务代码 } catch (Exception e) { // TODO: handle exception } //主动捕捉异常导致框架无法捕获,从而导致事物失效 }