文档章节

Spring实践--spring事务:基础知识

spinachgit
 spinachgit
发布于 02/15 15:12
字数 3384
阅读 112
收藏 12

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

一:基础知识介绍

1.1:事务四个特性:ACID

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么全部失败。
  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

1.2 隔离级别错误说明

  1. 脏读:并发事务之间一个事务读取了另一个事务还未提交的数据。
  2. 不可重复读:并发事务间一个事务多次读取,分别读取了另一个事务未提交和已经提交的数据,导致多次读取的数据状态不一致。
  3. 幻读:并发事务间一个事务读取了另一个事务已经提交的数据。与不可重复读的区别是不可重复读前后读取的是同一数据项,而幻读是一批数据。比如前后读取的数据数量不一致。

1.3 数据库四大隔离级别

  1. Serializable(串行化):可避免脏读、不可重复读、幻读的发生。
  2. Repeatable Read(可重复读):可避免脏读、不可重复读的发生。
  3. Read Committed(读已提交):可避免脏读的发生。
  4. Read unCommitted:(读未提交):最低级别,任何情况都会发生。

1.4 Spring隔离级别属性

  1. TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  2. TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。
  3. TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  4. TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  5. TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

1.5 Spring事务的传播行为

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

Spring定义了七种传播行为:

  1. TransactionDefinition.PROPAGATION_REQUIRED:如果当前事务存在,则加入当前事务;如果不存在,则创建一个新的事务。一般默认此项。
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前事务存在,则挂起当前事务。
  3. TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事物;如果事务不存在,则以无事务方式执行。
  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式执行,如果当前事务存在,则挂起当前事务。
  5. TransactionDefinition.PROPAGATION_NEVER:以非事务方式执行,如果存在事务,则抛出异常。
  6. TransactionDefinition.PROPAGATION_MANDATORY:表示该方法必须在事务中运行,如果当前存在事务,则加入当前事务;如果不存在,则抛出异常。
  7. TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个新的事务作为当前事务内嵌事务执行,嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果不存在,则取该值等价于TransactionDefinition.PROPAGATION_REQUIRED,即创建一个新的事务

注:以下具体讲解传播行为的内容参考自Spring事务机制详解

此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction。

1.6 Spring事务分类

  • 声明式事务:即为在配置文件中配置,无需程序员手动编程控制事务,也就是说数据库的事务的开启,提交都是框架帮助我们做好的
  • 编程式事务:是需要在方法中加入Spring的事务API  例如hibernate中的  beginTransaction() commit() rollback, 更加具有细粒度,但是同时也增加了代码的倾入性!

二:核心接口

Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。 
Spring事务管理涉及的接口的联系如下:

2.1 事务管理器

Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:

Public interface PlatformTransactionManager()...{  
    // 由TransactionDefinition得到TransactionStatus对象
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
    // 提交
    Void commit(TransactionStatus status) throws TransactionException;  
    // 回滚
    Void rollback(TransactionStatus status) throws TransactionException;  
} 

从这里可知具体的具体的事务管理机制对Spring来说是透明的,它并不关心那些,那些是对应各个平台需要关心的,所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、JPA。下面分别介绍各个平台框架实现事务管理的机制。

2.1.1 JDBC事务

如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
<property name="dataSource" ref="dataSource" />  
</bean>

实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。

2.1.2 Hibernate事务

如果应用程序的持久化是通过Hibernate实习的,那么你需要使用HibernateTransactionManager。对于Hibernate3,需要在Spring上下文定义中添加如下的<bean>声明:

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
<property name="sessionFactory" ref="sessionFactory" />  
</bean>

sessionFactory属性需要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。

2.1.3 Java持久化API事务(JPA)

Hibernate多年来一直是事实上的Java持久化标准,但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。如果你计划使用JPA的话,那你需要使用Spring的JpaTransactionManager来处理事务。你需要在Spring中这样配置JpaTransactionManager:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
	<property name="sessionFactory" ref="sessionFactory" />  
</bean>

JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。

2.1.4 Java原生API事务

如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">  
	<property name="transactionManagerName" value="java:/TransactionManager" />  
</bean>

JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。

2.2 基本事务属性的定义

上面讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。 
那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:

而TransactionDefinition接口内容如下:

public interface TransactionDefinition {
	// 返回事务的传播行为  
	int getPropagationBehavior();
	// 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据  
	int getIsolationLevel();
	// 返回事务必须在多少秒内完成  
	int getTimeout();
	// 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的  
	boolean isReadOnly();
}

我们可以发现TransactionDefinition正好用来定义事务属性,下面详细介绍一下各个事务属性。

2.2.1 Spring 事务实现原理

在应用系统调用 事务声明 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据事务配置信息,这个代理对象决定该声明事务的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器 AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。

Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,以CglibAopProxy 为例,对于CglibAopProxy,需要调用其内部类的DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,则调用其 invoke 方法。

三:遇到的问题

3.1:如果只是加了@Transactional注解,事务并不生效。

  • 事务注解就否开启:
    • xml:<tx:annotation-driver transaction-manager="transactionManager">
    • java:@EnableTransactionManagement 开启事务支持后。
  • 这个注解若是加载方法上,只能加在public的方法上,加到其他如private上不会报错,但是事务控制就不会生效。
  • mysql数据库:myisam存储引擎不支持事务,换成innodb

 

3.2 Spring事务中需要注意的问题

事务失效问题

在同一个代理对象内部,事务方法之间的直接嵌套调用,普通方法和事务方法之间的直接嵌套调用,都会造成事务异常!具体表现为某些传播行为不生效或者直接事务控制不生效。

@Service
public class DemoService {  

    @Transaction
    public void transactionMethod1()  
    {  
        op1();
        op2();
        ...
    }  
    public void commonMethod(){  
        ...
        transactionMethod1();  //照顾基础不牢的朋友,这里相当于 this.transactionMethod1(); 
        ...  
    }  


    @Transaction
    public void transactionMethod2(){  
        ...
        this.transactionMethod3();
        ...
    }  

    @Transaction(propagation= Propagation.REQUIRES_NEW)
    public void transactionMethod3()  
    {  
        op3();
        ...
    }  
} 


上面代码中,如果调用 DemoService 的 bean 对象的commonMethod ,则transactionMethod1里定义的事务将不生效(比如op2发生错误时,并不会回滚op1的操作),bean 调用 transactionMethod2时,transactionMethod2时里面调用的transactionMethod3也不会开启新的事务。

为什么会这样?
上面的实现机制中讲到,AOP的实现都是通过动态代理来实现,而AOP限制了我们只能在目标方法的开始和结束作为切点做切入处理增强。当动态代理对象最终调用的原始对象的目标方法时,并不能干预到目标方法内的方法调用行为,如果原始对象直接调用(用this.xxx方式)其他同类方法时,实际调用的是原始对象自身的方法,而不是代理类对象增强后(增加事务控制后)的方法。此时Spring对方法事务的控制(包括事务的传播行为、事务的隔离级别等)完全失效。

如何解决?
要想解决此类问题,主要都在于原始对象在调用对象内其他方法时,不要使用this.xxx的方式直接调用,通过注入或者获取代理对象的方式,使用代理对象调用需要调用的方法。下面列举几个解决方式:

1.注入自身,使用代理对象调用

@Service
public class DemoService {  

    @Autowired
    DemoService demoService;

    @Transaction
    public void transactionMethod1() {  
        op1();
        op2();
        ...
    }  

    public void commonMethod() {  
        ...
        //this.transactionMethod1() -> demoService.transactionMethod1()  
        demoService.transactionMethod1();  
        ...  
    } 
} 



2.使用AopContext,获取当前代理对象

@Service
public class DemoService {

    @Transaction
    public void transactionMethod1()  
    {  
        op1();
        op2();
        ...
    }  

    public void commonMethod()  
    {  
        ...
        //this.transactionMethod1() -> ((DemoService)AopContext.currentProxy()).transactionMethod1();) 
        ((DemoService)AopContext.currentProxy()).transactionMethod1();
        ...  
    }  

    ...
} 


3.使用BeanFactory获取代理对象(代码略)
 

参考博客:

Spring事务配置的五种方式 : 

spring 事务传播行为实例分析

Spring事务管理(详解+实例)

SpringBoot之事务处理机制

SpringBoot使用事务和AOP

 

 

 

 

© 著作权归作者所有

spinachgit
粉丝 10
博文 269
码字总数 272692
作品 0
亳州
架构师
私信 提问
极速上手进阶大神的 5 门 SpringBoot 课程

SpringBoot 因其大大简化了配置。可快速启动一个应用的开发环境,把时间和精力更多的投入到实现业务需求上去。成为了Java 服务器端近几年特别火爆的框架。 如此火爆的技术,怎能不抢先了解一...

实验楼
08/16
0
0
springboot 中使用事务

直接在service 层的方法上加上@Transactional 注解就ok。 注意事项: 1、Spring 基于注解的声明式事物 @Transactional 默认情况下只会对运行期异常(java.lang.RuntimeException及其子类)和 ...

不开心时不要学习
2018/01/12
3.2K
3
当 Spring Cloud 遇上 SOFAStack | Meetup#2 回顾

本文作者:玄北(曹杰),蚂蚁金服 SOFAStack 开源组核心成员。 本文根据 5月26日 SOFA Meetup#2 上海站 《当 Spring Cloud 遇上 SOFAStack》主题分享整理,主要来聊聊 spring-cloud-antfin ...

SOFAStack
06/13
97
0
那些年,我们一起追的Spring

学无止境,但仍需及时总结。 自去年开始写作以来,写了一些关于Spring的文章,今天将它们汇总起来,一方面方便大家阅读,另一方面,也是一次小的复盘总结。 IOC 首先是Spring的IOC,也就是控...

SexyCode
2018/08/14
0
0
Spring Cloud 劝退指南

我相信你一定是被标题吸引进来的,但是内容绝对不会让你失望,不要问我为什么?看了就知道。 做公众号也有一年多了,偶尔也会接几篇软文,赚点奶粉钱。 无论有没有软文,文章都会持续输出的,...

尹吉欢
06/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

006-ELK的二进制安装

二进制安装ELKstack 本次搭建属于单点,在同一台机器上进行安装 基础组件部署 curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo wget -O /etc/yu...

伟大源于勇敢的开始
今天
5
0
OSChina 周四乱弹 —— 浙江台的电话号码倒是记得挺牢

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @巴拉迪维 :《Out On The Streets》 一直不理解北欧人对重金属和死亡摇滚的热情,听完这首歌好像理解了。#今日歌曲推荐# 《Out On The Stre...

小小编辑
今天
12
0
Leetcode PHP题解--D121 21. Merge Two Sorted Lists

D121 21. Merge Two Sorted Lists 题目链接 21. Merge Two Sorted Lists 题目分析 合并两个有序链表。 思路 逐个遍历两个链表,把小的数字塞入数组里。之后再拼起来。 最终代码 <?php/** *...

skys215
今天
6
0
mars-config 动态配置管理

mars-config 码云地址:https://gitee.com/fashionbrot/mars-config 介绍 spring mvc 、springboot 动态配置系统。http 轮训方式 更新 动态配置 软件架构 软件架构说明 后端使用技术 :sprin...

fashionbrot
今天
15
0
女朋友玩吃鸡手游被开挂老哥骗炮,我见义勇为将骗子绳之以法

大家好,我是乔哥。 晚上10点以后下班后我回到自如出租房里面,开始处理公众号粉丝发来的消息,一条一条处理,突然看到了这么几条消息,吸引了我的眼球: 然后我就和这位女粉丝小红(化名)聊...

gzc426
今天
14
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部