前言

我们都知道Spring给我们提供了很多抽象,比如我们在操作数据库的过程中,它为我们提供了事物方面的抽象,让我们可以非常方便的以事物方式操作数据库。不管你用JDBC、Mybatis、Hibernate等任何一种方式操作数据库,也不管你使用DataSource还是JTA的事物,Spring事物抽象管理都能很好的把他统一在一起。接下来看一下事物的抽象核心接口

Spring事务抽象

PlatformTransactionManager是事物管理器接口

//事务管理器接口有以下几个接口,获取事物信息,提交和回滚
public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; void commit(TransactionStatus var1) throws TransactionException; void rollback(TransactionStatus var1) throws TransactionException;
}

常见的事物管理器有以下几种

  • DataSourceTransactionManager
  • HibernateTransactionManager
  • JtaTransactionManager

    这些管理器都实现了PlatformTransactionManager中的三个接口,实现逻辑略有差别,但是对用户来讲区别不大

定义事物的一些参数:

一些事物的参数在TransactionDefinition.java中,详情如下:

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;
}

下面两张图对这些参数进行了说明:

7种事务传播特性:



四种事务隔离级别:

在看事务隔离级别前需要先了解下什么是脏读、不可重复读、幻读

脏读: 脏读就是一个事物未提交的数据,被另外一个事物读到了,显然这种情况不可接受

不可重复读: 不可重复读是指在一个事务内,多次读同一数据,前后读取的结果不一致。

幻读: 事务A对表中的一个数据进行了修改,这种修改涉及到表中的全部数据行。同时事务B也修改了这个表中的数据,这种修改是向表中插入一行新数据。那么就会发生操作事务A的用户发现表中还存在没有修改的数据行,就好像发生了幻觉一样

知道了以上几个概念,我们来看看隔离级别:

这里我们可以看到,Spring并不是提供了所有的事物管理的实现,而是提供了标准的事物管理器的操作接口PlatformTransactionManager, 并且规范了其行为,具体的事物实现由各个平台自行实现。这就是Spring的事物抽象。

Spring之编程式事物

Spring提供了TransactionTemplate工具类可以很方便的使用编程式事务。默认情况下TransactionTemplate使用的是DataSourceTransactionManager。

在Spring上下文中,我们不配置TransactionTemplate这个bean,也能获取到TransactionTemplate。比如下面的例子。

@Service
public class UserInfoService { @Resource
private UserInfoDAO userInfoDAO;
@Autowired
private TransactionTemplate transactionTemplate; public void updateUser1(){
transactionTemplate.execute(transactionStatus -> {
userInfoDAO.updateUserName(1,"zhangsanfeng");
transactionTemplate.execute(transactionStatus2 -> {
userInfoDAO.updateUserName(2,"lisi");
return null;
});
return null;
});
}
}

由于Spring默认的事物传播特性是PROPAGATION_REQUIRED,我们来做一下验证,看是不是这样





上面两幅图可以看出,TransactionStatus中的newTransaction属性,第一个是true,第二个是false,正好符合PROPAGATION_REQUIRED所描述的情况。其他的传播特性可以自己去验证。

声明式事物

除了编程式事物外,Spring还为我们提供了声明式事物。使用@Transactional注解。

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该注解来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

@Transactional的rollbackFor属性可以设置一个 Throwable 的数组,用来表明如果方法抛出这些异常,则进行事务回滚。默认情况下如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚。

下面的代码事物就不会生效:

    @Transactional
public void updateUser2() throws Exception {
int r1 = userInfoDAO.updateUserName(1,"wanger");
int r2 = userInfoDAO.updateUserName(2,"mawu");
if (r2==1){
throw new Exception();
}
}

如果我们把抛出的异常改成RuntimeException,这时候事物就会生效了。或者指定异常让事物生效,比如 @Transactional(rollbackFor = Exception.class),这样碰到所有的异常事物都会生效了。

为什么加了@Transactional注解事物就生效了?

这是因为Spring容器会为加了这个注解的对象生成一个代理对象,实际调用的时候,实际上是调用的代理对象。 代理对象的实现了AOP的增强,实现了事物的实现。

通过注解怎么实现指定的传播特性和隔离级别的?

public @interface Transactional {
@AliasFor("transactionManager")
String value() default ""; @AliasFor("value")
String transactionManager() default ""; String[] label() default {}; Propagation propagation() default Propagation.REQUIRED; Isolation isolation() default Isolation.DEFAULT; int timeout() default -1; String timeoutString() default ""; boolean readOnly() default false; Class<? extends Throwable>[] rollbackFor() default {}; String[] rollbackForClassName() default {}; Class<? extends Throwable>[] noRollbackFor() default {}; String[] noRollbackForClassName() default {};
}

代码中可以看出,我们可以指定隔离级别和传播特性,在Spring为我们生成代理类的时候,会读取这些属性,体现在增强逻辑中。

事物失效的8种情况及解决办法

数据库引擎不支持事务

这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB,这时候选择支持事物的数据库即可(好像是废话,哈哈哈)

没有被 Spring 管理

这个好像没什么可说的,脱离了Spring的管理,还谈什么Spring事物管理。

方法不是 public 的

@Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

数据源没有配置事务管理器

相当于没开启事务管理,如果不是Springboot情况需要进行如下操作。

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}

如果是SpringBoot,在启动类上直接加上注解@EnableTransactionManagement即可。

传播特性配错了

传播特性配置成,Propagation.NOT_SUPPORTED或者Propagation.NOT_SUPPORTED,改成支持事物的传播特性即可。

异常类型错误

因为默认的异常类型是运行时异常,如果抛出了其他异常就不生效。

解决方式:

1、将异常改成运行时异常

2、指定异常进行事物回滚,如:@Transactional(rollbackFor = Exception.class)

异常被吃掉了

如果你代码这么写,事物不生效:

    @Transactional(rollbackFor = Exception.class)
public void updateUser2() {
int r1 = userInfoDAO.updateUserName(1,"3");
int r2 = userInfoDAO.updateUserName(2,"4");
if (r2==1){
throw new RuntimeException();
}
try { }catch (Exception e){ }
}

解决办法: 必须要抛出异常,否则Spring事务管理,不会走到回滚逻辑

类内部调用

@Service
public class UserInfoService {
public void justUpdate(){
updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() { }
}

上述代码不生效,因为内部调用不会涉及到代理类的调用,更不会有AOP的增强,因此不会生效。

解决办法:

1、自注入

@Service
public class UserInfoService {
@Autowired
private UserInfoService userInfoService;
public void justUpdate(){
userInfoService.updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() { }
}

2、Spring上下文

@Service
public class UserInfoService {
ApplicationContext applicationContext;
public void justUpdate(){
UserInfoService userInfoService = (UserInfoService) applicationContext.getBean("userInfoService");
userInfoService.updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() {
}
}

3、获取他的代理类,直接调用代理类

@Service
public class UserInfoService {
public void justUpdate(){
((UserInfoService) AopContext.currentProxy()).updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() {
}
}

----------------------------END---------------------------

更多Spring相关知识,请关注我,各平台都是同一个ID

Spring系列之事物是如何管理的的更多相关文章

  1. Spring中的事物管理----HelloWorld

    在学习Spring的事物管理之前明白先明白几个概念1什么是事物:事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用例子说明:例如银行转账,A账户转账(转2 ...

  2. Spring系列.事务管理

    Spring提供了一致的事务管理抽象.这个抽象是Spring最重要的抽象之一, 它有如下的优点: 为不同的事务API提供一致的编程模型,如JTA.JDBC.Hibernate和MyBatis数据库层 ...

  3. Spring系列之Spring常用注解总结 转载

    Spring系列之Spring常用注解总结   传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点:1.如果所有的内容都配置在.xml文件中,那么.x ...

  4. Spring系列(六):Spring事务源码解析

    一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...

  5. Spring 系列: Spring 框架简介 -7个部分

    Spring 系列: Spring 框架简介 Spring AOP 和 IOC 容器入门 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级 ...

  6. Spring 系列: Spring 框架简介(转载)

    Spring 系列: Spring 框架简介 http://www.ibm.com/developerworks/cn/java/wa-spring1/ Spring AOP 和 IOC 容器入门 在 ...

  7. Spring系列

    Spring系列之访问数据库   阅读目录 一.概述 二.JDBC API的最佳实践 三.Spring对ORM的集成 回到顶部 一.概述 Spring的数据访问层是以统一的数据访问异常层体系为核心,结 ...

  8. 通俗化理解Spring3 IoC的原理和主要组件(spring系列知识二总结)

    ♣什么是IoC? ♣通俗化理解IoC原理 ♣IoC好处 ♣工厂模式 ♣IoC的主要组件 ♣IoC的应用实例 ♣附:实例代码 1.什么是IoC(控制反转)? Spring3框架的核心是实现控制反转(Io ...

  9. 【SSH框架】之Spring系列(一)

    微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1.前言 前面更新过几篇关于 Struts2 框架和 Hibernate 框架的文章,但鉴于这两 ...

随机推荐

  1. ETL数仓测试

    前言 datalake架构 离线数据 ODS -> DW -> DM https://www.jianshu.com/p/72e395d8cb33 https://www.cnblogs. ...

  2. python安全编程之指纹识别

    什么是cms CMS是Content Management System的缩写,意为"内容管理系统",这是百度百科的解释,意思是相当于网站的建站模板,整个网站架构已经集成好了,只需 ...

  3. Java 使用 Apache commons-math3 线性拟合、非线性拟合实例(带效果图)

    Java 使用 CommonsMath3 的线性和非线性拟合实例,带效果图 例子查看 GitHub Gitee 运行src/main/java/org/wfw/chart/Main.java 即可查看 ...

  4. SQL语句(一)基础查询与过滤数据

    目录 一.数据库测试表 二.基础查询 1. 获得需要的记录的特定字段 2. 查询常量值 3. 查询表达式 4. 查询函数 5. 起别名 6. 去重 7. CONCAT函数的简单使用 三.过滤数据 大纲 ...

  5. CF201C Fragile Bridges TJ

    本题解依旧发布于洛谷,如果您能点个赞的话--(逃 前言 题目链接 正解:动态规划 思路不是很好想,想出来了应该就没有多大问题了,但是需要处理的细节较多,再加上水水的样例,难度应该是偏难的.个人感觉应该 ...

  6. Android面试6家一线大厂,这个问题是必问!

    年后面了六家大厂,每家都会问的一个问题就是Android的消息机制!可见Android的消息机制是多么重要! 消息机制之所以这么重要是因为Android应用程序是通过消息来驱动的,Android某种意 ...

  7. C++实现链表的相关基础操作

    链表的相关基础操作 # include <iostream> using namespace std; typedef struct LNode { int data; //结点的数据域 ...

  8. JavaGUI画笔工具的使用

    JavaGUI画笔工具的使用 package GUI; import java.awt.*; public class TestPaint { public static void main(Stri ...

  9. VueApp 自动更新解决plus is not defined问题

    一,今天用VueApp 做自动更新调用按照网上列子直接 Plus 打包编译后出现了plus is not defined 发现需要引用document.addEventListener("p ...

  10. JavaScript学习03(函数)

    函数 函数定义 JavaScript 函数是通过 function 关键词定义的. 声明定义 function functionName(parameters) { 要执行的代码 } 被声明的函数不会 ...