一般在Service会进行很多业务处理,复杂的业务可能需要调用几个Service的方法才能搞定,如果一个方法里面出现异常,可能就需要其他方法里面的操作都不生效。这时我们需要使用事务管理。

基本使用
在XML配置

  1. <!-- 事务管理器 -->
  2. <bean id="transactionManager"
  3. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  4. <!-- 数据源 -->
  5. <property name="dataSource" ref="dataSource" />
  6. </bean>
  1. <tx:annotation-driven transaction-manager="transactionManager" />

在代码里面使用注解管理事务

  1. @Transactional
  2. public void test(){}

propagation配置事务的传播行为

  1. 事务1——begin
  2. 事务2---begin
  3. 事务2---end (如果能执行这里事务2已经结束了,数据库里面的表如果有改动已经生效了)
  4. 1/0 这里出现异常
  5. 事务3---begin
  6. 事务3---end
  7. 事务1--end

�如果在一个方法里面开启事务1,在事务1里面分别调用其他方法,开启事务2和3。如果在中间出现异常,事务2就会执行成功,而事务1会回滚。这样就会有问题。我们更希望大的事务1是原子性的是一个整体。要么同时执行成功要么同时执行失败。这时我们可以通过配置propagation指定当事务嵌套时,如何的进行管理事务。

propagation常见取值
image.png

  • REQUIRED (当前有事务,支持当前事务,当前没有事务,开启新的事务) ```java 事务1——begin

    事务2—-begin (支持当前事务,如果事务1有事务,我就不开启事务。如果事务1没有开启新事务,就开启新的事务) 事务2—-end

    1/0 这里出现异常

    事务3—-begin 事务3—-end

事务1—end

  1. 这种情况非常适用于添加、删除、修改操作。
  2. - SUPPORTS (当前有事务,支持当前事务,当前没有,也不开新的事务)
  3. ```java
  4. 事务1——begin
  5. 事务2---begin (如果事务1有事务,我就用事务1的,如果事务1没有事务,我也不开新的)
  6. 事务2---end
  7. 1/0 这里出现异常
  8. 事务3---begin
  9. 事务3---end
  10. 事务1--end
  • REQUIRES_NEW 无论如何都开启新的,通常用于日志记录 ```java 事务1——begin

    事务2—-begin (无论事务1有没有事务,我都开启新的) 事务2—-end

    1/0 这里出现异常

    事务3—-begin 事务3—-end

事务1—end

  1. <a name="bbUbO"></a>
  2. ## rollbackFor
  3. 默认情况下RuntimeException、Error会导致事务回滚,而Exception不会。
  4. 我们可以通过设置rollbackFor的值来指定哪些异常会导致事务回滚
  5. ```java
  6. rollbackFor = Exception.class

表示在RuntimeException、Error的基础上增加Exception异常回滚,此时如果有Exception类型的异常也会回滚。

noRollbackFor

设置哪些异常不会导致事务回滚,在Exception的基础上增加的

isolation

事务隔离级别:如果多个事务同时操作同一份数据,可能引发以下问题

  • 脏读:一个事务读取到了一个事务还没有提交的数据 | id | name | age | | —- | —- | —- | | 1 | Rose | 20 | | 2 | Cliff | 18 |

一个A事务插入数据

  1. INSERT INTO user (name,age) values ("Jack",18)
  2. //.....
  3. commit()

一个B事务读取数据

  1. SELECT * FROM user

在A事务还没有执行commit之前,B事务已经读取到A事务插入的数据了,这种情况我们称为脏读。

  • 不可重复读:一个事务范围内,两个相同的查询却返回了不同的数据

一个A事务进行更新操作

  1. UPDATE user SET name="Jack",age=35 WHERE id = 1
  2. commit()

一个B事务进行读取操作。

  1. SELECT * FROM WHERE id= 1
  2. //....
  3. SELECT * FROM WHERE id= 1

当B事务进行第一次读取操作时,发现名字是Rose,年龄是20。A事务开始执行更新操作,操作完成后。B事务又进行了查询操作,发现读取的数据名字变成了Jack,年龄变成了35。都在一个事务范围执行相同的查询却返回了不同的数据,这种情况称为不可重复读。

  • 幻读:一个事务发现了之前本来不存在的数据

一个事务A在读取数据,比如有2条。此时其他数据进行插入了一条,再次读取时发现变了3条

  1. SELECT COUNT(*) FROM user #2
  2. //....
  3. SELECT COUNT(*) FROM user #3

不可重复读和幻读到底有什么区别呢?
(1) 不可重复读是读取了其他事务更改的数据,针对update操作
解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。
(2) 幻读是读取了其他事务新增的数据,针对insert和delete操作
解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。
这时候再理解事务隔离级别就简单多了呢。

在开发中使用Spring的默认选项基本就可以了