目录

JPA 和事务管理

使用 Spring @Transactional

@Transactional是什么意思?

EntityManager 何时跨越多个数据库事务?

什么定义了 EntityManager 与 Transaction 关系?

@PersistenceContext 是如何工作的?

那么@Transactional 是如何工作的呢?

交易方面

事务管理器

EntityManager 代理

把它们放在一起

结论

示例


想知道 Spring @Transactional 是如何工作的?了解实际情况。

在这篇文章中,我们将深入探讨 Spring 事务管理。我们将讨论如何 @Transactional真正在引擎盖下工作。其他即将发布的帖子将包括:

  • 如何使用传播和隔离等功能
  • 主要的陷阱是什么以及如何避免它们

JPA 和事务管理

重要的是要注意 JPA 本身不提供任何类型的声明性事务管理。在依赖注入容器之外使用 JPA 时,需要由开发人员以编程方式处理事务:

UserTransaction utx = entityManager.getTransaction(); try { utx.begin(); businessLogic();utx.commit();
} catch(Exception ex) { utx.rollback(); throw ex;
} 

这种管理事务的方式使得事务的范围在代码中非常清晰,但是它有几个缺点:

  • 它重复且容易出错
  • 任何错误都会产生非常大的影响
  • 错误很难调试和重现
  • 这会降低代码库的可读性
  • 如果这个方法调用另一个事务方法怎么办?

使用 Spring @Transactional

使用 Spring  @Transactional,上面的代码被简化为:

@Transactional
public void businessLogic() {
... use entity manager inside a transaction ...
}

这更加方便和可读,并且是目前在 Spring 中处理事务的推荐方式。

通过使用 @Transactional,事务传播等许多重要方面都可以自动处理。在这种情况下,如果另一个事务方法由 调用 businessLogic(),该方法将可以选择加入正在进行的事务。

一个潜在的缺点是这种强大的机制隐藏了幕后发生的事情,使得当事情不工作时很难调试。

@Transactional是什么意思?

其中一个关键点 @Transactional是需要考虑两个不同的概念,每个概念都有自己的范围和生命周期:

  • 持久性上下文
  • 数据库事务

事务注释本身定义了单个数据库事务的范围。数据库事务发生在持久性上下文的范围内。

持久性上下文在 JPA 中 EntityManager,使用 Hibernate 在内部实现 Session(当使用 Hibernate 作为持久性提供者时)。

持久化上下文只是一个同步器对象,它跟踪一组有限的 Java 对象的状态,并确保这些对象上的更改最终被持久化回数据库。

这是一个与数据库事务非常不同的概念。一个实体管理器 可以跨多个数据库事务使用,而且实际上经常如此。

EntityManager 何时跨越多个数据库事务?

最常见的情况是应用程序使用 Open Session In View 模式来处理延迟初始化异常,请参阅上一篇博客文章了解它的优缺点。

在这种情况下,在视图层中运行的查询与用于业务逻辑的查询在不同的数据库事务中,但它们是通过相同的实体管理器进行的。

另一种情况是开发人员将持久性上下文标记为 PersistenceContextType.EXTENDED,这意味着它可以承受多个请求。

什么定义了 EntityManager 与 Transaction 关系?

这实际上是应用程序开发人员的选择,但使用 JPA 实体管理器最常见的方式是使用 
“每个应用程序事务的实体管理器”模式。这是注入实体管理器的最常见方式:

@PersistenceContext
private EntityManager em;

默认情况下,我们处于“每个事务的实体管理器”模式。在这种模式下,如果我们在一个 @Transactional方法中使用这个实体管理器,那么该方法将在单个数据库事务中运行。

@PersistenceContext 是如何工作的?

想到的一个问题是,如何 @PersistenceContext在容器启动时只注入一次实体管理器,因为实体管理器的寿命很短,而且每个请求通常有多个。

答案是它不能: EntityManager是一个接口,注入到 spring bean 中的不是实体管理器本身,而是 一个上下文感知代理,它将在运行时委托给具体的实体管理器。

通常用于代理的具体类是 
SharedEntityManagerInvocationHandler,这可以在调试器的帮助下确认。

那么@Transactional 是如何工作的呢?

实现的持久性上下文代理 EntityManager并不是使声明式事务管理工作所需的唯一组件。实际上需要三个单独的组件:

  • EntityManager 代理本身
  • 交易方面
  • 事务管理器

让我们回顾一下每一个,看看它们是如何相互作用的。

交易方面

Transactional Aspect 是一个'around' 方面,在带注释的业务方法之前和之后都被调用。实现方面的具体类是 TransactionInterceptor.

事务方面有两个主要职责:

  • 在“之前”时刻,方面提供了一个挂钩点,用于确定将要调用的业务方法是否应该在正在进行的数据库事务的范围内运行,或者是否应该启动一个新的单独事务。
  • 在“之后”时刻,方面需要决定事务是应该提交、回滚还是继续运行。

在“之前”时刻,事务方面本身不包含任何决策逻辑,如果需要,启动新事务的决策被委托给事务管理器。

事务管理器

事务管理器需要回答两个问题:

  • 是否应该创建一个新的实体管理器?
  • 是否应该启动一个新的数据库事务?

这需要在调用事务方面“之前”逻辑时决定。事务管理器将根据以下因素做出决定:

  • 一项交易是否已经在进行的事实
  • 事务方法的传播属性(例如 REQUIRES_NEW总是启动一个新事务)

如果事务管理器决定创建一个新事务,那么它将:

  • 创建一个新的实体管理器
  • 将实体管理器绑定到当前线程
  • 从数据库连接池中获取一个连接
  • 将连接绑定到当前线程
  • 实体管理器和连接都使用 ThreadLocal变量绑定到当前线程。

它们在事务运行时存储在线程中,由事务管理器在不再需要时清理它们。

需要当前实体管理器或连接的程序的任何部分都可以从线程中检索它们。一个程序组件就是 EntityManager 代理。

EntityManager 代理

EntityManager 代理(我们之前介绍过)是最后一块拼图。例如,当业务方法调用时 
entityManager.persist(),此调用不是直接调用实体管理器。

相反,业务方法调用代理,该代理从事务管理器放置它的线程中检索当前实体管理器。

现在知道了 @Transactional机制的活动部分是什么,让我们回顾一下实现这项工作所需的常用 Spring 配置。

把它们放在一起

让我们来看看如何设置使事务注释正常工作所需的三个组件。我们首先定义实体管理器工厂。

这将允许通过持久性上下文注释注入实体管理器代理:

@Configuration
public class EntityManagerFactoriesConfiguration {@Autowiredprivate DataSource dataSource;@Bean(name = "entityManagerFactory")public LocalContainerEntityManagerFactoryBean emf() {LocalContainerEntityManagerFactoryBean emf = ...emf.setDataSource(dataSource);emf.setPackagesToScan(new String[] {"your.package"});emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());return emf;}
}

下一步是配置事务管理器并在带 @Transactional注释的类中应用事务方面:

@Configuration
@EnableTransactionManagement
public class TransactionManagersConfig {@AutowiredEntityManagerFactory emf;@Autowiredprivate DataSource dataSource;@Bean(name = "transactionManager")public PlatformTransactionManager transactionManager() {JpaTransactionManager tm = new JpaTransactionManager();tm.setEntityManagerFactory(emf);tm.setDataSource(dataSource);return tm;}
}

注解 @EnableTransactionManagement告诉 Spring 带有 @Transactional注解的类应该用 Transactional Aspect 包装。有了这个, @Transactional现在可以使用了。

结论

Spring 声明式事务管理机制非常强大,但它很容易被误用或错误配置。

在解决机制根本不工作或以意外方式工作的情况时,了解它的内部工作原理很有帮助。

要记住的最重要的事情是,实际上有两个概念需要考虑:数据库事务和持久性上下文,每个概念都有自己的不明显的生命周期。

未来的文章将讨论事务注释最常见的陷阱以及如何避免它们。

示例

EnableTransactionManagement (Spring Framework 5.3.16 API)

每日必读DZone Spring:Spring @Transactional 是如何真正工作的?相关推荐

  1. 每日必读DZone news - 2022年2月十大DZone文章

    带有示例的 20 大 Git 命令 每个工程领导者都应该阅读的 10 本书 16 条最佳编程名言 目前市场上最好的 Node.js IDE 学习 R:如何从数据框中提取行和列 Java 字符串格式示例 ...

  2. 每日必读DZone News—什么是敏捷真实的含义?

    每日坚持必读,就是紧随时代发展的步伐,技术之路虽艰辛,但终会有所收获.每天进步一小步,程序的世界已然不同.Java Zone成就每个程序员的不同.英文原文地址:https://dzone.com/ar ...

  3. 每日必读DZone News—对DevOps的关注

    每日坚持必读,就是紧随时代发展的步伐,技术之路虽艰辛,但终会有所收获.每天进步一小步,程序的世界已然不同.Java Zone成就每个程序员的不同.英文原文地址:https://dzone.com/ar ...

  4. 每日必读DZone News—Java中的随机数生成

    每日坚持必读,就是紧随时代发展的步伐,技术之路虽艰辛,但终会有所收获.每天进步一小步,程序的世界已然不同.Java Zone成就每个程序员的不同.英文原文地址:https://dzone.com/ar ...

  5. Spring提取@Transactional事务注解的源码解析

    声明:本文是自己在学习spring注解事务处理源代码时所留下的笔记: 难免有错误,敬请读者谅解!!! 1.事务注解标签 <tx:annotation-driven /> 2.tx 命名空间 ...

  6. Spring的@Transactional注解踩坑

    @Transactional介绍 Spring为开发人员提供了声明式事务的使用方式,即在方法上标记@Transactional注解来开启事务.大家在日常的开发中很多业务代码对数据进行操作的时候一定是希 ...

  7. Spring的Transactional注解

    Spring的Transactional注解主要有以下功能: 1. 标注在方法上,如果该方法掉了多个别的方法,每个方法都有对数据库做数据更改,如果这些更改需要保持一致性,这时就可以用到这个注解. 2. ...

  8. 【Spring】Spring高级话题-@Enable***注解的工作原理

    @EnableAspectJAutoProxy 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. @EnableAspectJAutoProxy注解 ...

  9. 接近8000字的Spring/Spring常用注解总结

    前言 这篇文章介绍的 Spring/SpringBoot 常用注解基本已经涵盖你工作中遇到的大部分常用的场景.对于每一个注解我都说了具体用法,掌握搞懂,使用 SpringBoot 来开发项目基本没啥大 ...

最新文章

  1. java后台访问接口
  2. 在ASP.NET中创建自定义控件初步(转)
  3. vscode 好用插件
  4. window 下 Atom 侧边栏字体大小设置
  5. C语言程序设计教程的读后感,《高质量c语言编程》读后感
  6. AspNet MVC2 学习笔记
  7. SVN回滚代码时,提示冲突怎么办
  8. 2017年高校网络信息安全管理运维挑战赛部分题解
  9. iOS小技能:模拟鼠标点击(针对Mac)
  10. navicat for mysql 10.1.7下载破解(2017.12.30)
  11. sudo_拔剑-浆糊的传说_新浪博客
  12. 智能机器人软件开发入门教程:带你从0到1快速入门
  13. 支付宝小程序开发练习,显示自定义二维码(四)
  14. python pandas 组内排序、单组排序、标号
  15. 股票代码中OF与SZ的区别
  16. openfoam后处理求全床平均孔隙率
  17. linux不显示联网图标_Ubuntu linux 面板网络图标消失--》寻回图标方法总结
  18. alienskineyecandy mac
  19. OpenCV Using Python——HSV颜色空间和改进的YCbCr颜色空间中的肤色检测
  20. 批处理下载MODIS数据

热门文章

  1. screenX、clientX、pageX, offsetX的区别
  2. screenX、clientX、pageX三者间的区别
  3. Excel中万能的查询函数——VLOOKUP
  4. 佳博80系列小票打印机 gp 80180安装驱动,显示打印错误
  5. 2023计算机考研408参考答案
  6. 郑大计算机专业英语01章,郑州大学远程教育 《大学英语II》第01章在线测试
  7. 计算机二级考试考的什么内容,计算机二级考试内容考些什么
  8. 苹果CMS海螺模板4.0修复版带后台 附安装教程
  9. 为什么叫区块链存储?兼谈IPFSFilecoin, Chia
  10. NRF52832 SPI 资料收集