Java:Spring @Transactional工作原理
本文将深入研究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工作原理的更多相关文章
- 25.Spring @Transactional工作原理
转自:http://www.importnew.com/12300.html 本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: prop ...
- 事务之五:Spring @Transactional工作原理
本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的. JPA(Java Persistence API--java持久层)和事务管理 很重要的一点是JPA本身 ...
- 详解Java GC的工作原理+Minor GC、FullGC
详解Java GC的工作原理+Minor GC.FullGC 引用地址:http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html J ...
- java gc的工作原理、如何优化GC的性能、如何和GC进行有效的交互
java gc的工作原理.如何优化GC的性能.如何和GC进行有效的交互 一个优秀的Java 程序员必须了解GC 的工作原理.如何优化GC的性能.如何和GC进行有效的交互,因为有一些应用程序对性能要求较 ...
- Java Web程序工作原理
Web开发的最重要的基本功能是HTTP:Java Web开发的最重要的基本功是Servlet Specification.HTTP和Servlet Specitication对于Web Server和 ...
- Spring MVC工作原理(好用版)
Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...
- HTTPS那些事 用java实现HTTPS工作原理
HTTPS那些事 用java实现HTTPS工作原理 博客分类: java历险 今天被问到关于https原理的问题,结果由于知识掌握不牢靠,停留于表面,很多细节都无法回答清楚,于是决定把https的 ...
- Spring Session工作原理
本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/KCOFv0nRuymkX79-RZi9eg 作者:张正林 目录:1.引入背景2.使用方法3.工作流程 ...
- Java[4] Jetty工作原理介绍(转)
转自:https://www.ibm.com/developerworks/cn/java/j-lo-jetty/ Jetty 的工作原理以及与 Tomcat 的比较 Jetty 应该是目前最活跃也是 ...
随机推荐
- mysql千万级数据优化查询
我们在做一个项目,一个网站或一个app时,用户量巨增,当使用的mysql数据库中的表数据达到千万级时,可以从以下方面考滤优化: 1.在设计数据库表的时候就要考虑到优化 2.查询sql语句上的优化 3. ...
- Python基础知识3-函数、参数及参数解构
函数 函数定义.调用 函数参数 函数参数默认参数 函数参数默认值 可变参数 keyword-only参数 可变参数和参数默认值 函数参数 参数解构 练习: #编写一个函数,能够接受至少2个参数 def ...
- SPP-net原理解读
转载自:目标检测:SPP-net 地址https://blog.csdn.net/tinyzhao/article/details/53717136 上文说到R-CNN的最大瓶颈是2k个候选区域都要经 ...
- Socket通信例子
Server端 using System; using System.Collections.Generic; using System.ComponentModel; using System.Da ...
- redis哨兵(Sentinel)、虚拟槽分区(cluster)和docker入门
一.Redis-Sentinel(哨兵) 1.介绍 Redis-Sentinel是redis官方推荐的高可用性解决方案,当用redis作master-slave的高可用时,如果master本身宕机,r ...
- Git submodule - 子模块【转】
子模块 有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你独立开发的,用于多个父项目的库. 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使 ...
- Java爬取12306余票
一.前言 今年国庆和中秋一起放,虽然很欢快,但是没有票了!!! 于是自己倒腾了一个查询余票的小程序. 二.准备工作 1.先打开12306的页面 2.然后右键检查,点network 3.再点一下1230 ...
- BZOJ2527[Poi2011]Meteors——整体二分+树状数组
题目描述 Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The ...
- PHP基础之$_SERVER的详细参数与说明
这几天准备静下心来看看平时忽略的一些PHP基础知识,也算是一个复习吧. 今天准备复习的是$_SERVER这个变量. 说明:$_SERVER 是一个包含了诸如头信息(header).路径(path).以 ...
- jenkins持续集成原理
转载: 原文地址:http://www.2cto.com/kf/201609/544550.html 持续集成 开发中,我们经常遇到一些奇怪问题,比如本地可以编译成功的代码但是同事们更新代码后编译出错 ...