# 实验六 事务属性:事务隔离级别
1、视角需要提升
2、测试的准备工作
①思路
②EmpService中参与测试的方法
③junit中执行测试的方法
④搞破坏
3、执行测试
①测试读未提交
②测试读已提交
4、理论
①数据库访问的并发问题
②事务隔离级别
③Spring隔离级别配置

实验六 事务属性:事务隔离级别

1、视角需要提升

实验六 事务属性:事务隔离级别 - 图1

2、测试的准备工作

①思路

实验六 事务属性:事务隔离级别 - 图2

②EmpService中参与测试的方法

// readOnly = true把当前事务设置为只读
// @Transactional(readOnly = true)
public String getEmpName(Integer empId) {

  1. 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。

上一个实验 回目录 下一个实验