@Transactional详解
我们主要学习@Transactional注解当中的三个常见属性:
1.rollbackFor: 异常回滚特性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型.
2.Isolation:事务的隔离级别. 默认值为Isolation.DEFAULT
3.propagation: 事务的传播机制. 默认为Propagation.REQUIRED.
rollbackFor
@Transactional 默认只在遇到运行时异常和Error才会进行回滚, 非运行时异常不回滚. 即Exception的子类中, 除了RuntimeException及其子类.
之前我们通过制造一个运行时异常, 发现事务回滚了, 接下来把异常改为如下代码:
java
@Transactional
@RequestMapping("/registry")
public String registry(String name, String password) throws IOException {
//用户注册
userService.registryUser(name, password);
log.info("用户数据插入成功");
if(true) {
throw new IOException();
}
return "注册成功";
}
运行程序:
发现虽然程序抛出了异常, 但是事务仍被提交:
如果需要所有的异常都回滚, 需要配置@Transactional注解当中的rollbackFor属性, 通过rollbackFor这个属性指定出现何种异常类型时事务进行回滚.
java
@Transactional(rollbackFor = Exception.class)
运行代码, 观察现象:
发现这时事务进行了回滚.
结论:
在Spring事务管理中, 默认只在遇到运行时异常RuntimeException和Error时才会进行回滚.
如果需要回滚指定类型的异常, 可以通过rollbackFor属性来指定.
事务隔离级别
回顾: 并发读数据库可能引发的问题
1.脏读:指一个事务A读取到了事务B还没有提交的数据, 在之后事务B进行了修改, 这时候事务A读到的数据称为脏数据, 也叫脏读.(解决方案: 给写操作加锁, 即一个事务在没有提交之前, 不能被读取).
2.不可重复读**: 一个事务先后两次读取到的数据不一致, 可能是两次读取过程中插入了一个新的事务更新了原有的数据, 注:侧重于修改**(解决方案: 给读操作加锁, 即一个事务在没有读完的时候不会插入其它的数据).
3.幻读: 同一个事务中, 多次查询的结果集返回不一致 . 比如事务A查询了几列数据, 而事务B在此时又插入了新的几列数据(注:侧重于新增或删除),先前的事务在接下来的查询中, 就会发现几列是它先前所没有的, 这就是幻读. (解决方案: 进一步提高事务之间的隔离性, 即串行化).
MySQL数据库事务隔离级别
1.读未提交: 最低的隔离级别, 在该级别内可以看到其它事务未提交的数据.
因为其它未提交的数据可能会发生回滚, 但是在该级别下仍然是可以读到的, 这个读到的就是脏数据.
2.读已提交: 在该级别内, 一个事务只能读取其它事务中已提交的数据(相当于加写锁).
在该隔离级别内不会存在脏读的问题. 但由于在事务的执行中可以读取到其它事务提交的结果, 所以在不同时间读取到的数据可能不一致.
3.可重复读: 事务不会读到其它事务对已有数据的修改, 即使其它事务已经提交. 也就可以确保同一事务多次查询到的结果一致, 但是对于其它事务新插入的数据, 是可以感知到的. 这就引发了幻读问题. 可重复读, 是MySQL中默认的事务隔离级别(相当于加读锁和写锁).
比如此级别的事务正在执行时, 另一个事务中新增了一条数据, 但是由于每次读取的时候结果都一样, 所以导致查询不到这条数据.
4.串行化: 是事务的最高隔离级别, 它会强制事务排序, 使之不会发生冲突. 但是因为资源消耗很大, 所以不太常用.
Spring中的事务隔离级别:
1.Isolation.DEFAULT: 以连接的数据库的隔离级别为主.
2.Isolation.READ_UNCOMMITTED:读未提交
3.Isolation.READ_COMMITED: 读已提交.
4.Isolation.REPEATABLE_READ: 可重复读
5.Isolation.SERIALIZABLE: 串行化.
spring中的事务隔离级别也可以通过类似之前的rollbackFor的方式设置:
java
@Transactional(isolation = Isolation.READ_COMMITTED)
Spring事务传播机制
什么是事务传播机制
事务传播机制就是: 多个事务方法存在调用关系时, 事务是如何在这些方法间进行传播的.
比如有两个方法A, B都被@Transactional修饰, A方法调用B方法, 此时B方法运行时, 是加入A的事务, 还是创建一个新的事务呢?
这就涉及到了事务的传播机制.
事务隔离级别解决的是多个事务同时调用一个数据库的问题.
而事务传播机制解决的hi一个事务在多个节点(方法)中传递的问题
事务的传播机制有哪些?
@Transactional注解支持事务传播机制的设置, 通过propagation属性来指定传播行为.
Spring事务传播机制有以下7种(以下结合一对新人要结婚的例子来说明):
1.Propagation.REQUIRED: 默认的事务隔离级别. 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则创建一个新的事务.(需要有房子, 如果你有房, 我们就一起住, 如果你没房, 我们就一起买房).
2.Propagation.SUPPORTS: 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则继续以非事务的方式继续执行.(可以有房子, 如果你有房, 那就一起住, 如果没房, 那就租房).
3.Propagation.MANDATORY: 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则抛出异常.(必须有房子, 要求必须有房, 如果没房就不结婚).
4.Propagation.REQUIRES_NEW: 创建一个新的事务. 如果当前存在事务, 则把当前事务挂起.
(必须买新房, 不管你有没有房, 必须要两个人一起买房. 即使有房也不住)
5.Propagation.NOT_SUPPORTED: 以非事务的方式运行, 如果当前存在事务, 则把当前事务挂起. (不需要房, 不管你有没有房, 我都不住, 必须租房)
6,Propagation.NEVER: 以非事务的方式运行, 如果当前存在事务, 则抛出异常(不能有房子)
7.Propagation.NESTED: 如果当前存在事务, 则创建一个事务作为当前事务的嵌套事务运行, 如果没有事务, 则取值等价于Propagation.REQUIRED.(如果有房, 则以房子为根据地, 做点下生意.)