# 实验六 事务属性:事务隔离级别
1、视角需要提升
2、测试的准备工作
①思路
②EmpService中参与测试的方法
③junit中执行测试的方法
④搞破坏
3、执行测试
①测试读未提交
②测试读已提交
4、理论
①数据库访问的并发问题
②事务隔离级别
③Spring隔离级别配置
实验六 事务属性:事务隔离级别
1、视角需要提升
2、测试的准备工作
①思路
②EmpService中参与测试的方法
// readOnly = true把当前事务设置为只读
// @Transactional(readOnly = true)
public String getEmpName(Integer empId) {
return empDao.selectEmpNameById(empId);<br />}
@Transactional(readOnly = false)
public void updateEmpName(Integer empId, String empName) {
empDao.updateEmpNameById(empId, empName);<br />}
③junit中执行测试的方法
@Test
public void testTxReadOnly() {
String empName = empService.getEmpName(3);
System.out.println("empName = " + empName);
}
@Test
public void testIsolation() {
Integer empId = 2;<br /> String empName = "aaaaaaaa";
empService.updateEmpName(empId, empName);
}
④搞破坏
为了让事务B(执行修改操作的事务)能够回滚,在EmpDao中的对应方法中人为抛出异常。
public void updateEmpNameById(Integer empId, String empName) {
String sql = “update t_emp set emp_name=? where emp_id=?”;
jdbcTemplate.update(sql, empName, empId);
System.out.println(10 / 0);
}
3、执行测试
在 @Transactional 注解中使用 isolation 属性设置事务的隔离级别。 取值使用 org.springframework.transaction.annotation.Isolation 枚举类提供的数值。
①测试读未提交
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public String getEmpName(Integer empId) {
return empDao.selectEmpNameById(empId);<br />}
@Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = false)
public void updateEmpName(Integer empId, String empName) {
empDao.updateEmpNameById(empId, empName);<br />}<br />![](https://cdn.nlark.com/yuque/0/2021/png/25621085/1639845530816-f4716e84-ab6a-4062-a0f0-d91773c53cb2.png#)
测试结果:执行查询操作的事务读取了另一个尚未提交的修改。
②测试读已提交
@Transactional(isolation = Isolation.READ_COMMITTED)
public String getEmpName(Integer empId) {
return empDao.selectEmpNameById(empId);<br />}
@Transactional(isolation = Isolation.READ_COMMITTED, readOnly = false)
public void updateEmpName(Integer empId, String empName) {
empDao.updateEmpNameById(empId, empName);<br />}<br />测试结果:执行查询操作的事务读取的是数据库中正确的数据。
4、理论
①数据库访问的并发问题
- 脏读 :一个事务读到了另一个事务的未提交的数据
- 不可重复读 :一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致.
- 幻读 :一个事务读到了另一个事务已经提交的 insert或者delete 的数据导致多次查询结果不一致.
②事务隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作,提供了四种隔离级别来解决上述问题。从低到高依次为READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ以及SERIALIZABLE,隔离级别越低,性能越高,但是安全性越差。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ_UNCOMMITTED | √ | √ | √ |
READ_COMMITTED | × | √ | √ |
REPEATABLE_READ | × | × | √ |
SERIALIZABLE | × | × | × |
③Spring隔离级别配置
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
private final int value;<br /> private Isolation(int value) {<br /> this.value = value;<br /> }<br /> public int value() {<br /> return this.value;<br /> }<br />}
如果选择DEFAULT,默认值,由底层数据库自动判断应该使用什么隔离级别。
对于互联网高并发项目来说,如果采用隔离级别SERIALIZABLE,固然安全,担心性能会受到严重影响。此时一般将隔离级别降低,保证效率,再配合悲观锁、乐观锁等技术来保证安全性。
事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持。Oracle 支持的 2 种事务隔离级别:READ_COMMITED(默认) , SERIALIZABLE。MySQL 支持 4种事务隔离级别,默认REPEATABLE READ。