事务以及Spring的事务管理
一、什么是事务?
事务是逻辑上的一组操作,要么都执行,要么都不执行
二、事务的特性(ACID)
- 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性: 执行事务前后,数据保持一致;
- 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
- 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
三、事务并发的问题
在实际的系统中,存在多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发操作可能会导致以下的问题:
- 脏读(Dirty read): 一个事务读到了另一个事务未提交的数据;
- 丢失修改(Lost to modify): 两个事务T1和T2读入同一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失;
- 不可重复读(Unrepeatableread): 在同一个事务T1中,进行多次查询,两次查询之间T2对数据进行了
修改
,导致T1两次查询的结果不一致; - 虚读/幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)
插入
了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
为了避免事务并发造成的问题,设置了不同级别的隔离防止以上问题的出现,下面是对隔离级别与并发问题的对应关系:
并发问题/隔离级别 | 脏读( Dirty Read ) | 丢失修改(Lost to modify) | 不可重复读( NonRepeatable Read ) | 虚读/幻读(Phantom read) |
---|---|---|---|---|
读未提交( Read uncommitted ) | 可能 | 可能 | 可能 | 可能 |
读已提交( Read committed ) | 不可能 | 可能 | 可能 | 可能 |
可重复读( Repeatable read ) | 不可能 | 不可能 | 可能 | |
可串行化( Serializable ) | 不可能 | 不可能 | 不可能 |
四、JDBC的事务管理
Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务;当事务完成后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚。除此之外,从JDBC 3.0中还引入了Savepoint(保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。
五、Spring的事务管理
Spring中用于事务管理的三个核心接口分别是:
- PlatformTransactionManager
- TransactionDefinition
- TransactionStatus;
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为
。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。
Spring的事务管理有两种方式:
- 1、传统的
编程式事务管理
,即通过编写代码实现的事务管理; - 2、是基于 AOP 技术实现的
声明式事务管理
。
5.1、三大核心接口
PlatformTransactionManager
该接口是 Spring 提供的平台事务管理器,用于管理事务。该接口中提供了三个事务操作方法,具体如下。
- TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息。
- void commit(TransactionStatus status):用于提交事务。
- void rollback(TransactionStatus status):用于回滚事务。
Spring并不直接管理事务,而是提供了多种事务管理器 ,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 Spring事务管理器的接口是:org.springframework.transaction.PlatformTransactionManager ,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
Spring为不同的持久层框架提供了不同的PlatformTransactionManager接口实现。
事务管理器 | 说明 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManager | 用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理 |
org.springframework.orm.hibernate3.HibernateTransactionManager | 用于集成Hibernate框架时的事务管理,只支持Hibernate3+版本 |
org.springframework.orm.jpa.JpaTransactionManager | 用于集成JPA实现框架时的事务管理 |
... ... | ... ... |
TransactionDefinition
该接口是事务定义(描述)的对象,它提供了事务相关信息获取的方法,其中包括五个操作,具体如下。
- String getName():获取事务对象名称。
- int getIsolationLevel():获取事务的隔离级别。
- int getPropagationBehavior():获取事务的传播行为。
- int getTimeout():获取事务的超时时间。
- boolean isReadOnly():获取事务是否只读。
TransactionStatus
该接口是事务的状态,它描述了某一时间点上事务的状态信息。其中包含六个操作:
名称 | 说明 |
---|---|
void flush() | 刷新事务 |
boolean hasSavepoint() | 获取是否存在保存点 |
boolean isCompleted() | 获取事务是否完成 |
boolean isNewTransaction() | 获取是否是新事务 |
boolean isRollbackOnly() | 获取是否回滚 |
void setRollbackOnly() | 设置事务回滚 |
5.2事务传播行为
事务传播行为的使用是为了解决业务层方法之间互相调用的事务问题
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
支持当前事务的情况:
(重点)TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不支持当前事务的情况:
(重点)TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
(重点)TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED 是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
5.3编程式事务管理
1、TransactionTemplate依赖DataSourceTransactionManager,
2、DataSourceTransactionManager依赖连接池DataSource构造
所以配置文件中如下:
<!--配置事务管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理的模板:Spring为了简化事务管理的代码而提供的类-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
3、在需要事务的业务层中注入TransactionTemplate类
4、编程实现事务操作
具体代码为:
/**
* @version V1.0
* @ClassName: AccoutServiceImpl
* @Description: 转账业务层实现类
* @author: Dreamice
* @date
**/
public class AccoutServiceImpl implements AccountService {
//注入转账的DAO类
private AccoutDao accoutDao;
public void setAccoutDao(AccoutDao accoutDao) {
this.accoutDao = accoutDao;
}
// 注入事务管理的模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transfer(String out, String in, Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accoutDao.outMoney(out,money);
int i = 1/0;
accoutDao.inMoney(in,money);
}
});
}
}
5.4声明式事务管理
声明式事务管理方式一:基于TransactionProxyFactoryBean的方式
这种方式只能对一个业务类进行增强,所以企业开发中并不适合用
配置文件applicationContext.xml的配置:
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置业务层的代理-->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!--配置目标对象-->
<property name="target" ref="accountService"/>
<!--注入事务管理器-->
<property name="transactionManager" ref="transactionManager"/>
<!--注入事务属性-->
<property name="transactionAttributes">
<props>
<!--prop格式
* PROPAGATION
* ISOLATION
* readOnly
* -Exception
* +Exception :发生哪些异常实务不回滚
-->
<prop key="transfer">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
声明式事务管理方式二:基于AspectJ的XML方式
经常使用,一旦配置好之后,类上不再需要添加任何东西
配置文件applicationContext.xml的配置:
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--编写通知:对事务进行增强(通知),需要编写切入点和具体执行事务的细节-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 给切入点方法添加事务详情,name表示方法名称,*表示任意方法名称,propagation用于设置传播行为,read-only表示隔离级别,是否只读 -->
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut1" expression="execution(* cn.dreamice.spring.demo3.AccountService+.*(..))"/>
<!--切面:将切入点与通知整合-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
声明式事务管理方式三:基于注解的方式
配置简单灵活,需要在业务层类或者方法上添加一个@Transaction的注解
配置文件applicationContext.xml的配置:
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--注册事务管理的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
在业务层添加事务,可以在类或者方法上添加,如下:
@Transactional
public class AccoutServiceImpl implements AccountService {
//注入转账的DAO类
private AccoutDao accoutDao;
public void setAccoutDao(AccoutDao accoutDao) {
this.accoutDao = accoutDao;
}
//转账方法
@Transactional
public void transfer(String out, String in, Double money) {
accoutDao.outMoney(out,money);
int i = 1/0;
accoutDao.inMoney(in,money);
}
}
在使用 @Transactional 注解时,事务定义参数之间用“,”进行分隔。如下:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
参考资料
1、慕课网-Spring事务管理
2、掘金-可能是最漂亮的Spring事务管理详解
3、C语言中文网-Spring声明式事务管理
4、SF-Spring事务传播行为详解
事务以及Spring的事务管理的更多相关文章
- spring事务之——spring配置事务的五种方式
Spring配置文件中关于事务配置总是由三个部分组成,分别是DataSource.TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分. DataSo ...
- MySQL事务及Spring事务管理
事务,是在数据库中用于保证数据正确性的一种机制,涉及到很多概念以及不同的情况,这里做一个总结 相关概念 事务四特性(ACID) 原子性(Atomicity,或称不可分割性):要么全部完成或者全部不完成 ...
- spring对事务的配置
接下来我将给大家介绍spring事务配置的两种方式: 1.基于XML的事务配置.2.基于注解方式的事务配置. 前言:在我们详细介绍spring的两种声明式事务管理之前,我们需要先理解这些概念 1)sp ...
- SPRING的事务配置详解
spring事务配置的两种方式: 1.基于XML的事务配置.2.基于注解方式的事务配置. 前言:在我们详细介绍spring的两种声明式事务管理之前,我们需要先理解这些概念 1)spring的事务管理是 ...
- spring service事务传播
spring定义的事务行为有以下几种: REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务.这是最常见的选择. SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行. ...
- Spring的事务到底该给Dao配置还是给Service配置
Spring的事务到底该给Dao配置还是给Service配置 Spring事务为业务逻辑进行事务管理,保证业务逻辑上数据的原子性. 事务得根据项目性质来细分:事务可以设置到三个层面(dao层.serv ...
- 数据库事务和spring事务的区别
数据库事务和spring事务 本质上其实是同一个概念,spring的事务是对数据库的事务的封装,最后本质的实现还是在数据库,假如数据库不支持事务的话,spring的事务是没有作用的.数据库的事务说简单 ...
- spring的事务是什么?与数据库的事务是否一样
spring的事务是什么?与数据库的事务是否一样 先说一下什么是事务,事务:是对数据库的一些列操作. 之前一直觉得事务只针对于数据库当中,5种隔离级别,7种传播行为,后来才发现这是针对Spring的, ...
- Spring的事务管理
事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 ...
随机推荐
- 迅为iTOP-开发板-驱动-can和rfid配置
在迅为开发板中,在 4412,4418 以及 6818 中,有的开发板默认配置 RFID,有的默认配 置 CAN 驱动(IMX6 默认都配置). 本文档介绍如何配置 CAN 和 RFID 的驱动. 截 ...
- 四十一、LAMP与LNMP加速与缓存优化进阶实战下部
一.配置,在nginx和apache所在的服务器中: 1.配置:cd /application/php/lib/php.ini 1)extension_dir="/application/p ...
- LeetCode No.157,158,159
No.157 Read 用 Read4 读取 N 个字符 题目 给你一个文件,并且该文件只能通过给定的 read4 方法来读取,请实现一个方法使其能够读取 n 个字符. read4 方法: API r ...
- opencv模板匹配查找图像(python)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import cv2 import numpy as np from cv2 import COLOR_B ...
- Papa开启“点播孙子”模式,新型老年人服务能在国内扎根吗?
"互联网+"对多个行业的全面赋能和渗入,的确让我们的生活与工作处处充满了便利.很多"跑断腿"才能办的事,现在只要在PC.智能手机上滑动鼠标.点击屏幕就能轻松搞定 ...
- vue项目环境搭建与组件介绍
Vue项目环境搭建 """ node ~~ python:node是用c++编写用来运行js代码的 npm(cnpm) ~~ pip:npm是一个终端应用商城,可以换国内 ...
- 5)void万能指针
函数参数为空,定义函数时,可以使用void来修饰:int fun(void) 函数没有返回值:void fun(void) 不同定义void类型的普通变量:void a //原因是,无法确定类 ...
- Normal Probability Plots|outlier
6.4 Assessing Normality; Normal Probability Plots The normal probability plot is a graphical techniq ...
- Django学习之视图层
视图层 小白必会三板斧 HttpResponse render redirect django视图函数必须要给返回一个HttpResponse对象(render和redirect内部返回的也是一个Ht ...
- iOS 版本更新迭代
开发中我们可能会遇到这样的需求,当AppStore中有新版本迭代更新,在用户点开APP的时候弹框提醒客户去AppStore更新APP.这里面就有个关键点,判断当前APP与AppStore中的版本高低, ...