Spring @Transactional实际如何工作?

在本文中,我们将深入探讨Spring事务管理。 我们将讨论@Transactional@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 ,上述代码简化为:

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

这更加方便和易读,是当前在Spring中推荐的处理事务的推荐方法。

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

潜在的不利之处在于,这种强大的机制隐藏了幕后发生的事情,从而在无法正常工作时很难进行调试。

@Transactional的关键点之一是要考虑两个单独的概念,每个概念都有自己的范围和生命周期:

  • 持久性环境
  • 数据库事务

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

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

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

这与数据库事务的概念非常不同。 一个实体管理器可用于多个数据库事务 ,而实际上经常是这样。

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

最常见的情况是,当应用程序使用“打开视图中的会话”模式来处理延迟的初始化异常时,请参见上一篇博客文章,以了解其优缺点 。

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

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

什么定义了EntityManager vs Transaction关系?

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

@PersistenceContextprivate EntityManager em;

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

@PersistenceContext如何工作?

想到的一个问题是,鉴于实体管理器的生命周期如此短,并且每个请求通常有多个实体,因此@PersistenceContext如何在容器启动时仅注入一次实体管理器。

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

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

@Transactional如何工作?

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

  • EntityManager代理本身
  • 交易方面
  • 交易经理

让我们逐一检查一下,看看它们如何相互作用。

交易方面

事务方面是在注释的业务方法之前和之后都被调用的“周围”方面。 实现方面的具体类是TransactionInterceptor

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

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

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

交易经理

交易经理需要提供以下两个问题的答案:

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

这需要在调用事务方面“之前”逻辑时确定。 交易经理将根据以下内容做出决定:

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

如果交易经理决定创建新交易,则它将:

  • 创建一个新的实体经理
  • 将实体管理器绑定到当前线程
  • 从数据库连接池中获取连接
  • 将连接绑定到当前线程

实体管理器和连接都使用ThreadLocal变量绑定到当前线程。

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

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

EntityManager代理

最后一步是EntityManager代理(我们之前已经介绍过)。 以业务方法为例
entityManager.persist() ,此调用未直接调用实体管理器。

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

现在知道了@Transactional机制的哪些移动部分,让我们@Transactional这项工作所需的常规Spring配置。

全部放在一起

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

这将允许通过持久性上下文注释注入Entity Manager代理:

@Configurationpublic 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@EnableTransactionManagementpublic 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声明式事务管理机制非常强大,但是很容易被滥用或错误配置。

在机制无法正常工作或无法正常工作的情况下进行故障排除时,了解其内部工作方式将很有帮助。

要牢记的最重要的一点是,实际上有两个概念需要考虑:数据库事务和持久性上下文,每个都有其自身不容易显现的生命周期。

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

翻译自: https://www.javacodegeeks.com/2014/06/how-does-spring-transactional-really-work.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363130.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python3-开发进阶-RESTful 软件架构风格

一. 什么是RESTful REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识&#x…

mysql卸载重装总是卡在starting server这一选项

因为自己不小心把msyql给下载了,重装了一个5.7版本的可是在安装时卡在starting server这一部分,运行不下去。重写卸载重装仍然不成功,还是卡在starting server.无法继续下面的安装,查看日志也没有报错信息。 问题分析:…

两款开源Web功能测试工具

参考自:http://www.ibm.com/developerworks/cn/web/wa-aj-testing/twill:官网:http://twill.idyll.org/安装:easy_install twill运行:twill-sh文档:http://twill.idyll.org/windmill安装:easy_install wind…

箭头函数与普通函数的区别

箭头函数: let fun () > {console.log(lalalala); } 普通函数: function fun() {console.log(lalla); } 箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种只包含一个表达式,连{ ... }和return…

前端基础-HTML标记语言

阅读目录 一、 HTML标签与文档结构二、 HTML标签详细语法与注意点三、 HTML中标签分类四、 HTML注释 一、 HTML标签与文档结构 HTML作为一门标记语言,是通过各种各样的标签来标记网页内容的。我们学习HTML主要就是学习的HTML标签。 那什么是标签呢? #…

cf1199解题报告

目录 cf1199解题报告ABCDEFcf1199解题报告 发一波水题。 A 模拟 #include <bits/stdc.h> #define ll long long using namespace std; const int _1e67; int n,x,y,a[_]; int main() {scanf("%d%d%d",&n,&x,&y);for(int i1;i<n;i) scanf("…

带有Gradle的Docker容器分为4个步骤

您是否需要通过Java Web应用程序创建Docker映像&#xff1f; 您在使用Gradle吗&#xff1f; 如果是这样&#xff0c;那么您距Docker nivana仅4步之遥。 对于此示例&#xff0c;我将使用一个简单的Spring Boot应用程序。 您可以在我的名为galoshe的Github存储库中找到所有源代码…

ImagesSprite V1.1.1 Beta发布

ImagesSprite v 1.1 发布后有网友提出样式显示了&#xff0c;但不能复制&#xff0c;现做调整如下&#xff08;20090804&#xff09;&#xff1a; 1.修正主界面下方css样式不能复制。 2.修正生成时透明色没有保存。【下载ImagesSprite V1.1.1 Beta】- 点击:4次转载于:https://w…

使用Spark构建简单的RESTful API

免责声明 &#xff1a;这篇文章是关于名为Spark的Java微型Web框架的&#xff0c;而不是关于数据处理引擎Apache Spark的 。 在此博客文章中&#xff0c;我们将看到如何使用Spark构建简单的Web服务。 如免责声明中所述&#xff0c;Spark是受Ruby框架Sinatra启发的Java微型Web框…

cf1206解题报告

目录 cf1206解题报告ABCDE&#xff0c;Fcf1206解题报告 A 模拟 #include <bits/stdc.h> using namespace std; int n,m,a[1010],b[12910]; int dsr[1111]; int main() {scanf("%d",&n);for(int i1;i<n;i) scanf("%d",&a[i]),dsr[a[i]]1;s…

canvas时钟

<!DOCTYPE html><html><head lang"en"><meta charset"UTF-8"><title></title></head><body><canvas id"canvas" width"500" height"500">您的浏览器不支持&#xff…

第七周

这个作业属于哪个课程C语言程序设计 &#xff08;第三版&#xff09;这个作业要求在哪里2019春季第七周作业我的课程目标学习指针的运用这个作业在哪个具体方面帮助我实现目标这个作业让我知道了指针实用性参考文献无一、2019春第七周作业&#xff08;基础题&#xff09; 7-2 自…

很久没来了,嘿嘿 问候一下大家,O(∩_∩)O哈哈~

很久没来了&#xff0c;嘿嘿。发现上次发帖正好是一个月以前哦&#xff0c;嘿嘿。最近职位火热招聘&#xff0c;有意向的快联系哦&#xff0c;嘿嘿1. BI Technical PM BI Technical PM Job Location: Redmond Oversea education or working background. 2 …

Java EE 8 –为更多设备提供更多应用程序

如果我不喜欢夏天的一件事&#xff0c;那就是事实是没有太多要分享或谈论的新闻。 谁决定将Java Day Tokyo置于这一年的无聊时间里&#xff0c;谁干得不错&#xff0c;就给我一个机会撰写有关新的和即将到来的Java EE 8规范的博客文章&#xff0c;其中包含了更多的思想和建议。…

全局预处理与执行,作用域与作用域链

一.全局预处理 <!DOCTYPE html><html><head lang"en"><meta charset"UTF-8"><title></title></head><body><p>全局预处理&#xff1a;首先会创建一个词法环境(Lexical Environment),然后扫面全局里…

2090. 「ZJOI2016」旅行者 分治,最短路

2090. 「ZJOI2016」旅行者 链接 loj 思路 \((l,mid)(mid1,r)\).考虑跨过mid的贡献。 假设选的中间那条线的点为gzy,贡献为\(dis(x,gzy)dis(gzy,y)\) 那就计算n遍最短路,一次分治为\(n^2mlog{nm}\) 设Sn*m.矩阵的长度是不定的&#xff0c;每次取最长的边进行分治是最好的&#x…

Xshell连接Linux慢问题解决办法

由于各种原因&#xff0c;经常更换网络环境&#xff0c;然后发现&#xff0c;每次更换网络环境后&#xff0c;xshell连接虚拟机的rhel或者CentOS都几乎是龟速.... 今天专门查了一下解决方案&#xff1a; 原来是ssh的服务端在连接时会自动检测dns环境是否一致导致的&#xff0c;…

Gradle入门:依赖管理

即使不是没有可能&#xff0c;创建没有任何外部依赖关系的现实应用程序也是一项挑战。 这就是为什么依赖性管理是每个软件项目中至关重要的部分的原因。 这篇博客文章描述了我们如何使用Gradle管理项目的依赖关系。 我们将学习配置已使用的存储库和所需的依赖项。 我们还将通过…

牛客NOIP暑期七天营-提高组1

牛客NOIP暑期七天营-提高组1 链接 A 边权可为0就排序建一条链子。 但是边权不为0 除了第一个有0的不行。 x连向上一个比他小的数。 期间判断有无解。 #include <bits/stdc.h> #define ll long long using namespace std; const int _2e57; int read() {int x0,f1;char sg…

BZOJ.4009.[HNOI2015]接水果(整体二分 扫描线)

LOJBZOJ洛谷 又是一个三OJ rank1&#xff01;w \(Description\) &#xff08;还是感觉&#xff0c;为啥非要出那种题目背景啊-直接说不好么&#xff09; 给定一棵树和一个路径集合&#xff08;每条路径有一个权值&#xff09;。\(Q\)次询问&#xff0c;每次询问给定一条路径&am…