spring(六):事务
事务特性ACID
- 原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做;
- 一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏。
- 隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
- 持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失。
常见的问题
- 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的
- 脏读:一个事务看到了另一个事务未提交的更新数据
- 不可重复读:在同一事务中,多次读取同一数据却返回不同的结果(也就是有其他事务更改了这些数据)
- 幻读:一个事务在执行过程中读取到了另一个事务已提交的插入数据(即在第一个事务开始时读取到一批数据,但此后另一个事务又插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,即好像发生幻觉一样)
事务隔离级别
- 未提交读(Read Uncommitted):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
- 提交读(Read Committed):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不可能可能出现丢失更新、脏读,但可能出现不可重复读、幻读;
- 可重复读(Repeatable Read):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,可能可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
- 序列化(Serializable):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读。
隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。在实际项目开发中为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。
事务管理器
Spring框架支持事务管理的核心是事务管理器,通过实现策略接口PlatformTransactionManager支持各种数据访问框架的事务管理。
Spring 支持两种类型的事务管理:
- 编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
- 声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。
public interface PlatformTransactionManager {
// 根据给定的TransactionDefinition类型参数获取一个已经激活的事务或创建一个新的事务
// 返回值TransactionStatus对象代表了当前事务的状态
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
// 提交TransactionStatus参数代表的事务
void commit(TransactionStatus var1) throws TransactionException;
// 回滚TransactionStatus参数代表的事务
void rollback(TransactionStatus var1) throws TransactionException;
}
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 = 1; // 表明可以发生误读、不可重复读和虚读。
int ISOLATION_READ_COMMITTED = 2; // 表明能够阻止误读;可以发生不可重复读和虚读。
int ISOLATION_REPEATABLE_READ = 4; // 表明能够阻止误读和不可重复读;可以发生虚读。
int ISOLATION_SERIALIZABLE = 8; // 表明能够阻止误读、不可重复读和虚读。
int TIMEOUT_DEFAULT = -1;
int getPropagationBehavior(); // 获取事务传播行为
int getIsolationLevel(); // 获取事务隔离级别
int getTimeout(); // 获取事务超时时间
boolean isReadOnly(); // 获取事务是否是只读的
@Nullable
String getName(); // 获取事务名字
}
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction(); // 当前事务状态是否是新事务
boolean hasSavepoint(); // 当前事务是否有保存点
void setRollbackOnly(); // 设置当前事务应该回滚
boolean isRollbackOnly(); // 当前事务是否应该回滚
void flush(); // 刷新底层会话中的修改到数据库
boolean isCompleted(); // 当前事务否已经完成
}
声明式事务处理的实现分析
声明式事务的实现就是通过环绕增强的方式,在目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务。
- 读取和处理在IoC容器中配置的事务处理属性,并转化为事务处理幼的数据结构(TransactionAttribute)(使用通知器TransactionAttributeSourceAdvisor来完成事务处理属性的处理,和TransactionProxyFactoryBean拦截下来的事务方法的处理结合起来)
- 实现统一的事务处理过程,包含处理事务配置属性和与线程绑定完成事务处理的过程。(TransactionInfo和TransactionStatus记录和传递相关执行场景)
- 底层的事务处理实现。即PlatformTransactionManager接口的具体实现。
事务处理拦截器的配置
// TransactionProxyFactoryBean
@Override
// 在IoC容器完成bean的依赖注入时,通过bean的初始化方法被调用
// 自此,transactionInterceptor配置被启动并成为通知器Advisor的一部分
protected Object createMainInterceptor() {
...
if (this.pointcut != null) {
return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
}
else {
// Rely on default pointcut.
return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
}
}
// AbstractSingletonProxyFactoryBean
// 是事务处理完成aop配置的地方
public void afterPropertiesSet() {
// 检测bean
...else {
...
// 创建代理工厂对象
ProxyFactory proxyFactory = new ProxyFactory();
// 为ProxyFactory生成代理对象、配置通知器、设置代理接口
// 自此,拦截器被配置进代理对象中
proxyFactory.addAdvisor(...);
...
proxyFactory.setInterfaces(...);
this.postProcessProxyFactory(proxyFactory);
this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
}
}
事务处理配置的读入
在TransactionInterceptor进行依赖注入时,其父类TransactionAspectSupport设置TransactionAttribute。
public void setTransactionAttributes(Properties transactionAttributes) {
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
public void setProperties(Properties transactionAttributes) {
TransactionAttributeEditor tae = new TransactionAttributeEditor();
Enumeration<?> propNames = transactionAttributes.propertyNames();
while (propNames.hasMoreElements()) {
String methodName = (String) propNames.nextElement();
String value = transactionAttributes.getProperty(methodName);
tae.setAsText(value);
TransactionAttribute attr = (TransactionAttribute) tae.getValue();
addTransactionalMethod(methodName, attr);
}
}
事务处理拦截器的设计与实现
// TransactionInterceptor
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
// TransactionAspectSupport
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 根据事务属性决定采用的事务处理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 创建事务 ★
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 沿着拦截器链进行
retVal = invocation.proceedWithInvocation();
}...
finally {
// 事务信息更新
cleanupTransactionInfo(txInfo);
}
// 提交事务<<PlatformTransactionManager>>
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
...
}
else {
// A normal return value: will lead to a commit.
...
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
...
return result;
}
...
}
}
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取事务对象,创建TransactionStatus对象
status = tm.getTransaction(txAttr);
}
...
}
// 把TransactionStatus设置到TransactionInfo中,并绑定到当前线程
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
spring(六):事务的更多相关文章
- spring框架学习(六)AOP事务及spring管理事务方式之Template模板
概念 1.事务 1)事务特性:ACID 原子性 :强调事务的不可分割. 一致性 :事务的执行的前后数据的完整性保持一致. 隔离性 :一个事务执行的过程中,不应该受到其他事务的干扰. 持久性 :事务一旦 ...
- Spring(九)Spring对事务的支持
一.对事务的支持 事务:是一组原子操作的工作单元,要么全部成功,要么全部失败 Spring管理事务方式: JDBC编程事务管理:--可以控制到代码中的行 可以清楚的控制事务的边界,事务控制粒度化细(编 ...
- 跟着刚哥学习Spring框架--事务配置(七)
事务 事务用来保证数据的完整性和一致性. 事务应该具有4个属性:原子性.一致性.隔离性.持久性.这四个属性通常称为ACID特性.1.原子性(atomicity).一个事务是一个不可分割的工作单位,事务 ...
- 事务以及Spring的事务管理
一.什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行 二.事务的特性(ACID) 原子性: 事务是最小的执行单位,不允许分割.事务的原子性确保动作要么全部完成,要么完全不起作用: 一致性 ...
- Spring的事务管理
事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 ...
- spring笔记--事务管理之声明式事务
事务简介: 事务管理是企业级应用开发中必不可少的技术,主要用来确保数据的完整性和一致性, 事务:就是一系列动作,它们被当作一个独立的工作单元,这些动作要么全部完成,要么全部不起作用. Spring中使 ...
- (spring-第20回【AOP基础篇】)Spring与事务
要想了解Spring的事务,首先要了解数据库事务的基本知识,数据库并发会产生很多问题,Spring使用ThreadLocal技术来处理这些问题,那么我们必须了解Java的ThreadLocal技术.下 ...
- spring的事务操作
我们项目一期已经差不多结束了,所以一些细节也被拿了出来,出现最多的就是事务的操作了.因为自己负责的是一个模块(因为是另外一个项目的负责人),所以组员经常会遇到事务的问题,会出现很多奇葩的用法,各种乱用 ...
- Spring MVC 事务配置
Spring MVC事务配置 要了解事务配置的所有方法,请看一下<Spring事务配置的5种方法> 本文介绍两种配置方法: 一. XML,使用tx标签配置拦截器实现事务 一. ...
- Spring应用——事务管理
事务基础:请参看:http://www.cnblogs.com/solverpeng/p/5720306.html 一.Spring 事务管理 1.前提:事务管理器 在使用 Spring 声明式事务管 ...
随机推荐
- .NET Core 初次上手Swagger
安装NuGet 程序包=>Swashbuckle.AspNetCore 在 Startup.ConfigureServices 方法里添加注册生成器 //注册Swagger生成器,定义一个和 ...
- 嵊州D6T2 城市 city
城市 city [问题描述] 众所周知,why 是czyz 王国的国王. czyz 王国一共有n 个城市,每个城市都有一条道路连向一个城市(可能连向这个城市自己). 同时,对于每一个城市,也只有一条道 ...
- 小白月赛22 E : 方格涂色
E:方格涂色 考察点 : 思维,模拟 坑点 : long long 其他的好像没什么,读懂题意就可以 AC 不要被样例画的图所迷惑 Code: #include <vector> #inc ...
- 【android】Parcelable的相关技术总结
关于Parcelable的相关知识学习 进行Android开发的时候,无法将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然 ...
- nodeJS菜鸟教程笔记
http模块 var http = require('http'); // 引入http模块 var url = require('url'); // 引入url模块 var querystring ...
- 原生JS操作class 极致版
// 获取class function getClass(el) { return el.getAttribute('class') } // 设置class function setClass(el ...
- 另外一种获取redis cluster主从关系和slot分布的方法
条条大路通罗马,通过最近学习redis cluster 观察其输出,发现了另外一种获取master-slave关系的方法. [redis@lxd-vm1 ~]$ cat get_master_slav ...
- MCPS & MIPS
MIPS:Million Instructions Per Second MCPS:Million Cycles Per Second MIPS = Total Instructions*Sampli ...
- 利用 Hexo + Github 搭建自己的博客
扯在前面 在很久很久以前,一直就想搭建属于自己的一个博客,但由于各种原因,最终都不了了之,恰好最近突然有了兴趣,于是就自己参照网上的教程,搭建了属于自己的博客. 至于为什么要搭建自己的博客了?哈哈,大 ...
- 解决pjax重复加载js导致事件重复绑定的问题
个人博客 地址:http://www.wenhaofan.com/article/20180925232057 1.所有js统一在pjax容器外引入 在pjax容器外引入的js只会被引入一次,所以不会 ...