本文将深入研究Spring的事务管理。主要介绍@Transactional在底层是如何工作的。之后的文章将介绍:

  • propagation(事务传播)和isolation(隔离性)等属性的使用
  • 事务使用的陷阱有哪些以及如何避免

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(){
...your code
}

代码更加简洁,可读性更好,也是目前Spring中事务处理的推荐方式。

通过使用@Transactional,事务传播等很多重要方面可以自动处理。这种情况下如果businessLogic()调用了其他事务方法,该方法将根据选项确定如何加入正在运行事务。

这个强大机制的一个潜在缺点是它隐藏了底层的运行,当它不能正常工作时很难调试。

@Transactional含义

关于@Transactional,关键点之一是要考虑两个独立的概念,它们都有各自的范围和生命周期:

  • persistence context(持久化上下文)
  • database transaction(事务)

@Transactional本身定义了单个事务的范围。这个事务在persistence context的范围内。

JPA中的持久化上下文是EntityManager,内部实现使用了Hibernate Session(使用Hibernate作为持久化provider)。

持久化上下文仅仅是一个同步对象,它记录了有限集合的Java对象的状态,并且保证这些对象的变化最终持久化到数据库。

这是与单个事务非常不同的概念。一个Entity Manager可以跨越多个事务使用,而且的确是这样使用的。

EntityManager何时跨越多个事务?

最常见的情况是应用使用Open Session In View模式处理懒初始化异常时。

这种情况下视图层运行的多个查询处于独立的事务中,而不是单事务的业务逻辑,但这些查询由相同的entity manager管理。

另一种情况是开发人员将持久化上下文标记为PersistenceContextType.EXTENDED,这表示它能够响应多个请求。

如何定义EntityManager和Transaction之间的关系?

这由应用开发者来选择,但是JPA Entity Manager最常用的方式是“Entity Manager per application transaction”(每个事务都有自己的实体管理器)模式。entity manager注入的常用方法是:

@PersistenceContext
private EntityManager em;

这里默认为“Entity Manager per transaction”模式。这种模式下如果在@Transactional方法内部使用该Entity Manager,那么该方法将在单一事务中运行。

@PersistenceContext如何工作?

随之而来的问题就是@PersistenceContext如何仅在容器启动时注入entity manager,假定entity manager生命周期很短暂,而且每次请求需要多个entity manager。

答案是它不能:EntityManager是一个接口,注入到spring bean中的不是entity manager本身,而是在运行时代理具体entity manager的context aware proxy(上下文感知代理)。

通常用于代理的具体类为SharedEntityManagerInvocationHandler,借助调试器可以确认这一点。

那么@Transactional如何工作?

实现了EntityManager接口的持久化上下文代理并不是声明式事务管理的唯一部分,事实上包含三个组成部分:

  • EntityManager Proxy本身
  • 事务的切面
  • 事务管理器

看一下这三部分以及它们之间的相互作用。

事务的切面

事务的切面是一个“around(环绕)”切面,在注解的业务方法前后都可以被调用。实现切面的具体类是TransactionInterceptor。

事务的切面有两个主要职责:

  • 在’before’时,切面提供一个调用点,来决定被调用业务方法应该在正在进行事务的范围内运行,还是开始一个新的独立事务。
  • 在’after’时,切面需要确定事务被提交,回滚或者继续运行。

在’before’时,事务切面自身不包含任何决策逻辑,是否开始新事务的决策委派给事务管理器完成。

事务管理器

事务管理器需要解决下面两个问题:

  • 新的Entity Manager是否应该被创建?
  • 是否应该开始新的事务?

这些需要事务切面’before’逻辑被调用时决定。事务管理器的决策基于以下两点:

  • 事务是否正在进行
  • 事务方法的propagation属性(比如REQUIRES_NEW总要开始新事务)

如果事务管理器确定要创建新事务,那么将:

  • 创建一个新的entity manager
  • entity manager绑定到当前线程
  • 从数据库连接池中获取连接
  • 将连接绑定到当前线程

使用ThreadLocal变量将entity manager和数据库连接都绑定到当前线程。

事务运行时他们存储在线程中,当它们不再被使用时,事务管理器决定是否将他们清除。

程序的任何部分如果需要当前的entity manager和数据库连接都可以从线程中获取。

EntityManager proxy

EntityManager proxy(前面已经介绍过)就是谜题的最后一部分。当业务方法调用entityManager.persist()时,这不是由entity manager直接调用的。

而是业务方法调用代理,代理从线程获取当前的entity manager,前面介绍过事务管理器将entity manager绑定到线程。

了解了@Transactional机制的各个部分,我们来看一下实现它的常用Spring配置。

整合三个部分

如何将三个部分组合起来使事务注解可以正确地发挥作用呢?首先定义entity manager工厂。

这样就可以通过持久化上下文注解注入Entity Manager proxy。

@Configuration
public class EntityManagerFactoriesConfiguration{
@Autowired
private 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{
@Autowired
EntityManagerFactory emf; @Autowired
private DataSource dataSource; @Bean(name="transactionManager")
public PlatformTransactionManager transactionManager(){
JpaTransactionManager tm=
new JpaTransactionManager();
tm.setEntityManagerFactory(emf);
tm.setDataSource(dataSource);
return emf;
}
}

注解@EnableTransactionManagement通知Spring,@Transactional注解的类被事务的切面包围。这样@Transactional就可以使用了。

总结

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

当这个机制不能正常工作或者未达到预期运行结果等问题出现时,理解它的内部工作情况是很有帮助的。

需要记住的最重要的一点是,要考虑到两个概念:事务和持久化上下文,每个都有自己不可读的明显的生命周期。

Java:Spring @Transactional工作原理的更多相关文章

  1. 25.Spring @Transactional工作原理

    转自:http://www.importnew.com/12300.html 本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: prop ...

  2. 事务之五:Spring @Transactional工作原理

    本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的. JPA(Java Persistence API--java持久层)和事务管理 很重要的一点是JPA本身 ...

  3. 详解Java GC的工作原理+Minor GC、FullGC

    详解Java GC的工作原理+Minor GC.FullGC 引用地址:http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html J ...

  4. java gc的工作原理、如何优化GC的性能、如何和GC进行有效的交互

    java gc的工作原理.如何优化GC的性能.如何和GC进行有效的交互 一个优秀的Java 程序员必须了解GC 的工作原理.如何优化GC的性能.如何和GC进行有效的交互,因为有一些应用程序对性能要求较 ...

  5. Java Web程序工作原理

    Web开发的最重要的基本功能是HTTP:Java Web开发的最重要的基本功是Servlet Specification.HTTP和Servlet Specitication对于Web Server和 ...

  6. Spring MVC工作原理(好用版)

    Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...

  7. HTTPS那些事 用java实现HTTPS工作原理

    HTTPS那些事 用java实现HTTPS工作原理 博客分类: java历险   今天被问到关于https原理的问题,结果由于知识掌握不牢靠,停留于表面,很多细节都无法回答清楚,于是决定把https的 ...

  8. Spring Session工作原理

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/KCOFv0nRuymkX79-RZi9eg 作者:张正林 目录:1.引入背景2.使用方法3.工作流程 ...

  9. Java[4] Jetty工作原理介绍(转)

    转自:https://www.ibm.com/developerworks/cn/java/j-lo-jetty/ Jetty 的工作原理以及与 Tomcat 的比较 Jetty 应该是目前最活跃也是 ...

随机推荐

  1. MyBatis基础:MyBatis关联查询(4)

    1. MyBatis关联查询简介 MyBatis中级联分为3中:association.collection及discriminator. ◊ association:一对一关联 ◊ collecti ...

  2. nodejs开发辅助工具nodemon

    前面的话 修改代码后,需要重新启动 Express 应用,所做的修改才能生效.若之后的每次代码修改都要重复这样的操作,势必会影响开发效率,本文将详细介绍Nodemon,它会监测项目中的所有文件,一旦发 ...

  3. servlet篇 之 跳转问题

    servlet中的跳转: 跳转本质上是对文件传输操作 进行 封装. 例如:客户端访问一个servlet,在servlet中需要把已经写好的页面hello.html返回给客户端浏览器,需要用io来实现这 ...

  4. 【XSY3042】石像 拓扑排序 状压DP 洲阁筛

    题目大意 有 \(n\) 个整数 \(a_1,a_2,\ldots,a_n\),每个数的范围是 \([1,m]\).还有 \(k\) 个限制,每个限制 \(x_i,y_i\) 表示 \(a_{x_i} ...

  5. 【CF1132G】Greedy Subsequences(线段树)

    [CF1132G]Greedy Subsequences(线段树) 题面 CF 题解 首先发现选完一个数之后选择下一个数一定是确定的. 对于每个数预处理出左侧第一个比他大的数\(L\),那么这个数加入 ...

  6. NowCoder -- 牛客小白月赛10

    A--勘测 推下公式  a[i] = a[i-1] + a[i-2] +2 #include<stdio.h> #include<string.h> ]; int main() ...

  7. 20175209 《Java程序设计》第五周学习总结

    20175209 <Java程序设计>第五周学习总结 一.教材知识点总结 1.接口的定义 接口声明:关键字interface 接口体:public static final 修饰常量,pu ...

  8. 不得不用的提高效率小技巧让你用Mac更顺手| Mac小技巧(三)

    文章内容及图片来源于:知乎,如果涉及版权问题,请联系作者删除 文章收录于:风云社区(提供上千款各类mac软件的下载) 1. 用预览给GIF删帧 我们在给文章配图或者做表情包的过程中,常需要截取 GIF ...

  9. python Django cookie和session

    在一个会话的多个请求中共享数据,这就是会话跟踪技术.例如在一个会话中的请求如下:  请求银行主页: 请求登录(请求参数是用户名和密码): 请求转账(请求参数与转账相关的数据): 请求信誉卡还款(请求参 ...

  10. OpenStack虚拟机冷迁移与热迁移

    一.虚拟机迁移分析 openstacvk虚拟机迁移分为冷迁移和热迁移两种方式. 1.1冷迁移: 冷迁移(cold migration),也叫静态迁移.关闭电源的虚拟机进行迁移.通过冷迁移,可以选择将关 ...