一、Mysql事务特性与隔离级别
ACID特性
1.原子性(Atomicity)
是指事务是一个不可分割的单位,事务中的操作要么全部执行成功,要么全部执行失败
2.一致性(Consistency)
是指事务中的最后的状态为预期的状态,从一个一致性状态变成另一个一致性的状态
3.隔离性(Isolation)
是指事务中一个事务的操作不能被另一个事务操作所影响,多个并发事务要相互隔离
4.持久性
是指事务成功提交后,对数据操作的结果是永久性的
事务的隔离级别 由低到高
Mysql默认隔离级别是可重复读(repeatable-read)
事务隔离级别 | 脏读 | 不可重复度 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
二、Spring事务注解@Transactional的使用
1.定义事务的数据源
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
//定义事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
2.springConfig开启事务注解
@EnableTransactionManagement
public class SpringConfig {}
3.使用@Transactional注解
(1)定义在类上 表示该类中所有的public方法都使用事务
(2)定义在方法上 表示该方法使用事务注意非public修饰符的方法事务会失效
三、@Transactional注解配置项
属性 | 类型 | 描述 |
---|---|---|
value | String | 可以指定使用的事务管理器 |
propagation | enum: Propagation | 指定事务的传播行为 |
readOnly | boolean | 指定事务是否只读 |
isolation | enum: Isolation | 指定事务的隔离级别 |
timeOut | int(秒) | 指定事务的超时时间 |
rollbackFor | Class对象数组,必须继承自Throwable | 指定事务回滚的异常类 |
rollbackForClassName | 类名数组,必须继承自Throwable | 指定事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 指定事务不回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 指定事务不回滚的异常类名字数组 |
Spring默认RuntimeException 和Error错误才会回滚事务 所以可以指定异常类型或自定义异常类回滚事务
(1)timeout 超时设置
// timeout = -1 表示永不超时
// 30为默认时间
@Transactional(timeout = 30)
(2)readOnly 事务是否只读
// 默认false
// 在不需要事务的方法中可以定义为true
@Transactional(readOnly = false)
(3)isolation 事务的隔离级别配置
// MYSQL: 默认为REPEATABLE_READ级别
// 读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// 读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.READ_COMMITTED)
// 可重复读(会出现幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
// 串行化
@Transactional(isolation = Isolation.SERIALIZABLE)
(4)rollbackFor 触发事务回滚的异常类型,可以指定多个
// 如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚
@Transactional(rollbackFor=Exception.class)
(5)noRollbackFor 指定异常不回滚
// Exception异常不会回滚。runtimeException会回滚事务
@Transactional(rollbackFor=Exception.class)
(6)propagation 事务的传播行为
// 如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.REQUIRED)
// 不为这个方法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
// 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕后继续执行老的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
// 必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)
// 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
// 如果其他bean调用这个方法,在其他bean中声明事务那就用事务,如果其他bean没有声明事务,那就不用事务
@Transactional(propagation=Propagation.SUPPORTS)
四、事务失效的几种场景
(1)@Transactional 应用在非 public 修饰的方法上
(2)@Transactional 注解属性 propagation 设置错误
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
(3)@Transactional 注解属性 rollbackFor 指定异常类设置错误
(4)@Transactional 注解属性 rollbackFor 指定异常类设置错误
1.开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/**
* B 插入字段为 3的数据
*/
this.insertB();
/**
* A 插入字段为 2的数据
*/
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
(5)异常被你的 catch“吃了”导致@Transactional失效
@Transactional
private Integer A() throws Exception {
try {
/**
* B 插入字段为 3的数据
*/
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
A方法调用B方法 B方法抛出异常被A捕获了,事务不能回滚
(6)数据库不是Innodb引擎, 不支持事务
借鉴:
事务异常