【Spring Framework】Spring入门教程(八)Spring的事务管理
事务是什么?
事务:指单个逻辑操作单元的集合。
在操作数据库时(增删改),如果同时操作多次数据,我们从业务希望,要么全部成功,要么全部失败。这种情况称为事务处理。
例如:A转账给B。
第一步,扣除A君账号要转的金额。
第二步,增加B君账号的金额。
这两个步骤,要么都成功,要么都失败,这就是事务。
Spring事务控制我们要明确的
1.JavaEE体系进行分层开发,事务处理位于业务层,所以,一般情况下我们使用的事务代理(事务管理器)一般放在分层设计的业务层。
2.spring框架为我们提供了一组事务控制的API。
3.spring的事务控制都是基于AOP的,它既可以使用编程的方式实现(编程式事务),也可以使用配置的方式实现(声明式事务),声明式事务又可分为XML配置和注解配置两种。
案例引出问题
需求:从ID为10086账户给ID为10010账户转账1000元钱。
数据准备:account表(账户):
CREATE TABLE `t_account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`balance` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10087 DEFAULT CHARSET=utf8;
编写转账案例
转账案例分析
结论:根据上述案例分析,事务管理应该是在Service层处理。
数据库并发问题
什么是数据库并发问题?
并发:多个客户端同时访问数据库中某一条数据(秒杀)。
数据库可以拥有多个访问客户端,若多个客户端并发地访问数据库中相同的资源,如果没有采取必要的隔离措施,则会导致各种并发问题,破坏数据的完整性、一致性。
这些问题归结为5类:
包括3类数据读问题(脏读,不可重复读,幻读)
和2类数据更新问题(第一类丢失更新,第二类丢失更新)。 看图
1.5.1. 第一类丢失更新
两个事务更新相同数据,如果一个事务提交,另一个事务回滚,第一个事务的更新会被回滚。
1.5.2. 第二类丢失更新
多个事务同时读取相同数据,并完成各自的事务提交,导致最后一个事务提交会覆盖前面所有事务对数据的改变。
1.5.3. 脏读
第二个事务查询到第一个事务未提交的更新数据,第二个事务根据该数据执行后续的操作,但第一个事务回滚,第二个事务操作的就是脏数据。
1.5.4. 幻读
一个事务查询到了另一个事务已经提交的新数据,导致多次查询数据不一致
1.5.5. 不可重复读
一个事务查询到另一个事务已经修改的数据,导致多次查询数据不一致
1.5.6. 小结
【1】并发事务带来哪些问题?
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
脏读: 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
幻读: 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读: 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
【2】不可重复读和幻读区别
不可重复读的重点是修改,比如多次读取一条记录发现其中某些列的值被修改。
幻读的重点在于新增或者删除,比如多次进行条件查询发现记录增多或减少了。
数据库事务的隔离级别
问题:上述问题理论上如果出现了应该如何解决?
答:一般情况,数据库都会处理一些事务并发的问题,数据库提供了不同的事务隔离级别来处理不同的事务并发问题,事务隔离级别定义如下:
针对不同隔离级别可以解决的的如下五类问题
解决丢失更新的方案
数据库表数据加锁
1,悲观锁
(1) 在操作当前数据的事务开启事务就使用for update
锁住当前数据。
(2) Hibernate和MyBatis都有悲观锁对应的解决方案。
2,乐观锁
(1) 为表添加一个version字段。当前事务操作的时候都会比对当前事务的多次操作的版本号是否一致,如果不一致认为数据已经被更新,事务进行回滚。
(2) Hibernate和MyBatis都有乐观锁对应的解决方案。
Spring对事务的支持
- 为什么需要使用Spring事务?
答:Spring事务代理(事务管理器),已经将事务的具体代码封装好了,只需要在spring配置文件中配置一次,就不用重复编写事务处理代码,大大提高了开发效率,方便后期的维护!!
Spring框架针对事务处理提供专门的解决方案。
Spring的事务管理主要包括3个接口:
TransactionDefinition
该接口主要定义了:事务的传播行为(规则),事务的隔离级别,获得事务信息的方法。所以在配置事务的传播行为,事务的隔离级别,需要获得事务信息时,可以通过查阅该类的代码获得相关信息。
public interface TransactionDefinition {
//事务的传播行为(规则)
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
//事务的隔离级别
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
//事务超时管理
int TIMEOUT_DEFAULT = -1;
//获得事务信息
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}
1.7.1.1. 事务传播规则
Spring在TransactionDefinition接口中定义了七种事务传播规则,规定了事务方法和事务方法发生嵌套调用时事务该如何进行传播。
PlatformTransactionManager事务管理器
PlatformTransactionManager接口主要定义事务的处理方法,获取事务,开启事务,提交事务,回滚事务,在实际开发中不同的框架有不同的实现类。
Spring的事务管理:
1,PlatformTransactionManager:接口统一抽象处理事务操作相关的方法;
1):TransactionStatus getTransaction(TransactionDefinition definition):
根据事务定义信息从事务环境中返回一个已存在的事务,或者创建一个新的事务,并用TransactionStatus描述该事务的状态。
2):void commit(TransactionStatus status):
根据事务的状态提交事务,如果事务状态已经标识为rollback-only,该方法执行回滚事务的操作。
3):void rollback(TransactionStatus status):
事务回滚,当commit方法抛出异常时,rollback会被隐式调用
2,在使用spring管理事务的时候,首先得告诉spring使用哪一个事务管理器,使用不同的框架(JdbcTemplate,MyBatis,Hibernate/JPA ),使用事务管理器都不同。
1.7.2.1. PlatformTransactionManager事物管理器的继承体系图
3,常用的事务管理器:
DataSourceTransactionManager:使用JDBC,MyBatis的事务管理器;
Spring事务的配置
Spring支持编程式事务管理和声明式事务管理。
编程式事务管理:事务和业务代码耦合度太高。
声明式事务管理:侵入性小,把事务从业务代码中抽离出来,使用AOP配置到配置文件中,提高维护性。
声明式事务管理-xml方式配置
1.8.1.1. 准备配置文件
--在配置文件中引入新的命名空间tx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
1.8.1.2. 配置事物管理器-DataSourceTransactionManager
Spring对事务的支持,必须先配置事务管理器,事务管理器已经封装了事务的具体的处理
使用JDBC,MyBatis的事务管理器;(当前案例使用的Spring的JDBC操作,所以配置这个)。
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="${jdbc.maxActive}" />
</bean>
<!-- 1,配置事务管理器(应根据情况使用合适的事务管理器) -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 事务相关的配置 : 使用AOP面向切面编程(切事务) : 使用 tx: 标签 -->
<!-- tx:advice : 配置事物的标签
id : 唯一标识
transaction-manager : 需要用到的事物管理器
-->
<!-- 2,配置管理事务的“增强” :环绕通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 配置要切的方法 -->
<tx:method name="trans"/>
</tx:attributes>
</tx:advice>
<!-- 3,面向切面 : -->
<aop:config >
<!-- 切入点 : where -->
<aop:pointcut expression="execution ( * org.cjw.service..*.*(..))" id="pt"/>
<!-- 配置切面 切面 = 切入点 + 通知
advice-ref 通知的引用
pointcut-ref 切入点的引入
-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
<!-- 织入(Weaving):把切面加入到对象,并创建出代理对象的过程。(该过程由Spring来完成) -->
</aop:config>
1.8.1.3. 事物方法的属性细节配置
事务方法属性:
<!-- 2,配置管理事务的“增强” :环绕通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 配置要切的方法 -->
<!-- <tx:method>
name : 需要切面的方法
isolation : 事务的隔离级别 DEFAULT 使用当前数据库默认的隔离级别,不同数据库隔离级别不同(可以不配置)
propagation : 事物的传播规则 ,默认使用 REQUIRED
read-only : 是否是只读事务, DQL配置即可
-->
<!-- 一般DML操作才需要事务,DQL查询操作是不需要事物
* 通配符
-->
<!-- 所有以以下为前缀的方法都认为是查询方法,不切入事物 -->
<tx:method name="get*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="true" timeout="-1"/>
<tx:method name="find*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="true" timeout="-1"/>
<tx:method name="select*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="true" timeout="-1"/>
<tx:method name="query*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="true" timeout="-1"/>
<tx:method name="list*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="true" timeout="-1"/>
<!-- 非查询(DQL)方法: DML操作,需要事务管理 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
说明:首先切入点为业务层的所有方法,但是查询方法是不需要事务的,所以我们需要在事务管理器里面设置哪些方法需要使用事务管理(DML操作,除了查询以外),哪些方法无需(查询方法)。<tx:method>
标签就是用于过滤方法的,当设置为read-only=true时,表示这个方法是查询,无需使用事务管理器,相反,则需要。
声明式事务管理-基于注解配置
1.8.2.1. applicationContext.xml配置文件
<!-- 1,配置事务管理器(应根据情况使用合适的事务管理器) -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启注解驱动配置事务
编写此标签:Spring底层就支持注解配置事物,并且已经配置好事物管理器
-->
<tx:annotation-driven transaction-manager="txManager"/>
1.8.2.2. AccountServiceImpl 业务层代码
@Service
/* @Transactional
* 贴上此当前类已经被Spring事务管理
* 注意: @Transactional 只能对当前贴的Service类有效
* 常用属性 :
* isolation=Isolation.REPEATABLE_READ, 隔离级别
propagation=Propagation.REQUIRED,传播规则
readOnly=true 是否只读事务
*
*/@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao dao;
public void trans(Integer transOutId,
Integer transInId, Integer money) {
dao.tranOut(transOutId, money);
System.out.println(1 / 0);// 模拟断电
dao.tranIn(transInId, money);
}
//单独为某一个方法配置具体的事物细节:如查询方法,事物是只读的
@Transactional(readOnly=true)
public Object getUser() {
//查询操作
return null;
}
@Transactional(readOnly=true)
public List<Object> getUsers() {
//查询操作
return null;
}
}
事物配置的注解和XML配置的选择
Xml配置 : 代码清晰,配置量少,容易维护
注解配置 : 侵入代码,每个Service层类都得配置,针对不同的方法还得配置事务细节:如查询方法配置只读操作,不容易维护
建议选择xml配置
【Spring Framework】Spring入门教程(八)Spring的事务管理的更多相关文章
- Java框架spring 学习笔记(十八):事务管理(xml配置文件管理)
在Java框架spring 学习笔记(十八):事务操作中,有一个问题: package cn.service; import cn.dao.OrderDao; public class OrderSe ...
- Spring Boot2 快速入门教程-到上手
Spring Boot2 教程合集 入门 纯 Java 代码搭建 SSM 环境 创建一个 Spring Boot 项目的三种方法 理解 Spring Boot 项目中的 parent 基础配置 配置文 ...
- spring原理案例-基本项目搭建 01 spring framework 下载 官网下载spring jar包
下载spring http://spring.io/ 最重要是在特征下面的这段话,需要注意: All avaible features and modules are described in the ...
- RabbitMQ入门教程(八):远程过程调用RPC
原文:RabbitMQ入门教程(八):远程过程调用RPC 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.cs ...
- Spring Cloud 入门教程(八): 断路器指标数据监控Hystrix Dashboard 和 Turbine
1. Hystrix Dashboard (断路器:hystrix 仪表盘) Hystrix一个很重要的功能是,可以通过HystrixCommand收集相关数据指标. Hystrix Dashboa ...
- 笔记70 Spring Boot快速入门(八)(重要)
上传文件 一.方式一 1.上传页面 upLoadPage.html <!DOCTYPE html> <html lang="en"> <head> ...
- [Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.
前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习 ...
- spring boot 学习入门篇【spring boot项目的搭建以及如何加载jsp界面】
[ 前言] Spring Boot 简介:Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置, ...
- Spring Framework 学习笔记——核心技术之Spring IOC
Spring Framework 官网文档学习笔记--核心技术之Spring IOC 官方文档 spring-framework-5.3.9 1. Spring Framework 核心技术 1.1 ...
- SpringBoot入门教程(八)配置logback日志
Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logback-core,logback- classic和logback-access.logback-c ...
随机推荐
- 使用BadBoy录制JMeter脚本
BadBoy简介 BadBoy是一款免费WEB自动化测试工具,其实就是一个浏览器模拟工具,具有录制和回放功能,支持对录制出来的脚本进行调试.同时支持捕获表单数据的功能,所以能够进行自动化测试.但目前 ...
- BootStrap_1 浓缩版本
BootStrap(基于JQuery框架) 商业互吹:Bootstrap是最受欢迎的HTML.CSS和JS框架,用于开发响应式布局,移动设备优先选择的WEB项目... 特色:1.响应式布局 2.基于f ...
- TLFS 内存分配算法详解
文章目录 1. DSA 背景介绍 1.1 mmheap 1.2 mmblk 2. TLFS 原理 2.1 存储结构 2.2 内存池初始化 2.3 free 2.4 malloc 参考资料 1. DSA ...
- Emmet快速语法—助力HTML/CSS一行代码一个页面
学会之后牛掰的场景如下 我们的目标就是用一行代码=>写下面这样的长长长长的HTML结构来. 如:table>(thead.text>th{手机1}*4)+(tbody.text$*4 ...
- ES6基础知识(Map用法)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 更通俗的理解JS原型链
最近在网上看到一篇理解原型链的,感觉非常好非常通俗易懂,拿来记录一下~: 1)人是人他妈生的,妖是妖他妈生的.人和妖都是对象实例,而人他妈和妖他妈就是原型.原型也是对象,叫原型对象. 2)人他妈和人他 ...
- Codeforces 375C - Circling Round Treasures(状压 dp+最短路转移)
题面传送门 注意到这题中宝藏 \(+\) 炸弹个数最多只有 \(8\) 个,故考虑状压,设 \(dp[x][y][S]\) 表示当前坐标为 \((x,y)\),有且仅有 \(S\) 当中的物品被包围在 ...
- Comet OJ Contest #13 D
Comet OJ Contest #13 D \(\displaystyle \sum_{i=0}^{\left\lfloor\frac{n}{2}\right\rfloor} a^{i} b^{n- ...
- SP20173 DIVCNT2 - Counting Divisors (square)
Refer 主要思路参考了 Command_block 的题解. Description 给定 \(n\)(\(n\le 10^{10}\)),求 \[\sum_{i=1}^n\sigma_0(i^2 ...
- pheatmap() 的热图制作
1.数据准备 2.画图 3.参数调整 (转自百迈克公众号) 关注下方公众号可获得更多精彩