概述
什么是事务
事务是数据库操作最基本单元。逻辑上一组操作,要么都成功,如果有一个失败所有操
作都失败。
事务的四个特性
- 原子性 Atomicity
- 一致性 Consistency
- 隔离性 Isolation
- 持久性 Durability
搭建环境
模拟转账场景:Lucy给Mary转账,Lucy少钱,Mary多钱。
配置步骤
-
引入相关jar包
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
spring-jdbc-5.2.6.RELEASE.jar
spring-orm-5.2.6.RELEASE.jar
spring-tx-5.2.6.RELEASE.jar
引入后所有包:
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
spring-aop-5.2.6.RELEASE.jar
spring-aspects-5.2.6.RELEASE.jar
spring-beans-5.2.6.RELEASE.jar
spring-context-5.2.6.RELEASE.jar
spring-core-5.2.6.RELEASE.jar
spring-expression-5.2.6.RELEASE.jar
spring-jdbc-5.2.6.RELEASE.jar
spring-orm-5.2.6.RELEASE.jar
spring-tx-5.2.6.RELEASE.jar -
创建数据库和建表
数据库user_db、表t_account
插入数据后表结果:1
2
3
4id username money
------ -------- --------
1 Lucy 1000
2 Mary 1000 -
spring的配置文件注入连接池和jdbcTemplate
-
创建 service和dao并完成对象的创建和注入关系
-
在dao中创建多钱和少钱的方法,在service中创建转账的方法
示例代码
代码结构:
1 | └─src |
bean.xml:
1 | <!--组件扫描--> |
UserDao接口:
1 | public interface UserDao { |
UserDaoImpl类:
1 |
|
UserService类:
1 |
|
Test类:
1 | public class Test { |
运行后程序没有异常,数据库效果:
1 | id username money |
场景引入
上面的例子若运行有异常,如下所示:
1 | public void accountMoney(){ |
数据库效果:
1 | id username money |
Lucy少钱,而Mary没多钱。这时候就需要用到事务。
Sping事务管理介绍
- 一般把事务添加到service层
- 事务管理方式
编程式事务管理 :1、开启事务 2、执行业务逻辑 3、若业务逻辑没有异常提交事务,若有异常事务回滚。
声明式事务管理:1、基于注解实现 2、基于XML实现 - Spring的声明式事务底层用的是AOP原理
声明式事务管理
注解实现
配置步骤
-
在spring配置文件中配置事务管理器
-
在spring配置文件中开启事务注解
需要引入tx空间1
2xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd -
使用@Transactional注解开启事务。
如果把这个注解添加类上面,这个类里面所有的方法都添加事务,如果把这个注解添加方法上面,为这个方法添加事务。
Spring事务的传播行为
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
前两种比较常用,需要掌握。
spring事务的隔离级别
问题
事务有隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题,比如脏读、不可重复读和幻读。
- 脏读:一个未提交事务读取到另一个未提交事务的数据。
比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务–>取走100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读。 - 不可重复读:一个未提交事务读取到另一提交事务修改数据。一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
比如银行取钱,事务A开启事务–>查出银行卡余额为1000元,此时切换到事务B事务B开启事务–>事务B取走100元–>提交,数据库里面余额变为900元,此时切换回事务A,事务A再查一次查出账户余额为900元,这样对事务A而言,在同一个事务内两次读取账户余额数据不一致,这就是不可重复读。 - 幻读:一个未提交事务读取到另一提交事务添加或删除数据。
比如学生信息,事务A开启事务–>修改所有学生当天签到状况为false,此时切换到事务B,事务B开启事务–>事务B插入了一条学生数据,此时切换回事务A,事务A提交的时候发现了一条自己没有修改过的数据,这就是幻读,就好像发生了幻觉一样。幻读出现的前提是并发的事务中有事务发生了插入、删除操作。
解决方法
隔离级别/是否解决问题 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ_UNCOMMITTED(读未提交) | 否 | 否 | 否 |
READ_COMMITED(读已提交) | 是 | 否 | 否 |
REPEATABLE_READ(可重复读) | 是 | 是 | 否 |
SERLALIZABLE(串行化) | 是 | 是 | 是 |
Spring隔离级别DEFAULT将使用底层数据库的默认事务隔离级别。MySQL默认隔离级别是REPEATABLE_READ,代码如下:
1 |
|
Spring事务的其他参数
- timeout :超时时间
事务需要在一定时间内进行提交,如果不提交进行回滚;默认值是 -1表示不失效,设置时间以秒单位进行计算 - readOnly :是否只读
readOnly 默认值 false,可以进行增删查改操作;设置为true后只能进行查询操作 - rollbackFor :回滚
设置出现哪些异常进行事务回滚 - noRollbackFor :不回滚
设置出现哪些异常不进行事务回滚
示例代码如下:
1 |
XML实现
步骤
- 配置事务管理器
- 配置通知
- 配置切入点和切面
相关代码
java代码中去掉@Transactional注解,其他不变。spring配置文件代码如下:
1 | <!--组件扫描--> |