Spring管理的交易说明-第2部分(JPA)

在本系列的第一部分中 ,我展示了事务如何在普通JDBC中工作 。 然后,我展示了Spring如何管理基于JDBC的事务。 在本系列的第二部分中,我将首先展示事务如何在普通的JPA中工作。 然后展示Spring如何管理基于JPA的事务。

资金转移

为了帮助说明交易,我将使用同一案例研究,将资金从一个银行帐户转移到另一个银行帐户。 在这里,我们显示了借方,贷方和转账方法的代码片段。

... class BankAccountService {public void transfer(MonetaryAmount amount, ...) {debit(amount, ...);credit(amount, ...);...}public void credit(MonetaryAmount amount, AccountId accountId) {...}public void debit(MonetaryAmount amount, AccountId accountId) {...}...
}

JPA交易

在普通的JPA中,通过在EntityManager上调用getTransaction().begin()来启动事务。 下面的代码段对此进行了说明。

import javax.persistence.*;
...
EntityManagerFactory emf = ...;
EntityManager em = emf.createEntityManager();
try {em.getTransaction().begin();// make changes through entitiesem.getTransaction().commit();...
} catch(Exception e) {em.getTransaction().rollback();throw e;
} finally {em.close();
}

从技术上讲, EntityManager从创建时就处于事务中。 因此,调用begin()有点多余。 在调用begin()不能调用某些操作,例如persistmergeremove 。 查询仍然可以执行(例如find() )。

从查询返回的对象可以更改。 尽管JPA规范尚不清楚在没有事务开始时这些更改将发生什么。

现在,让我们将JPA应用于资金转移案例研究。

我们定义了一个BankAccount实体来处理debit()credit()行为。

import javax.persistence.*;@Entity
... class BankAccount {@Id ...;...public void debit(MonetaryAmount amount) {...}public void credit(MonetaryAmount amount) {...}...
}

我们将EntityManagerFactory添加到BankAccountService以在需要时启用EntityManager的创建。

import javax.persistence.*;... class BankAccountService {private EntityManagerFactory emf; // injected via constructor...public void transfer(MonetaryAmount amount, ...) ... {EntityManager em = emf.createEntityManager();try {em.getTransaction().begin();BankAccount fromAccount = em.find(BankAccount.class, ...);BankAccount toAccount = em.find(BankAccount.class, ...);fromAccount.debit(amount);toAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)} finally {em.close();}}public void credit(MonetaryAmount amount, AccountId ...) ... {EntityManager em = emf.createEntityManager();try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)} finally {em.close();}}public void debit(MonetaryAmount amount, AccountId ...) ... {EntityManager em = emf.createEntityManager();try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.debit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)} finally {em.close();}}
}

Spring管理的JPA交易

transfercreditdebit方法肯定可以使用模板类(类似于JdbcTemplate )来删除所有样板代码。 Spring以前提供了JpaTemplate类,但是从Spring 3.1开始不推荐使用,而推荐使用本机EntityManager用法(通常通过@PersistenceContext获得)。

因此,让我们做到这一点-使用通过@PersistenceContext获得的EntityManager

import javax.persistence.*;... class BankAccountService {@PersistenceContextprivate EntityManager em;...public void transfer(MonetaryAmount amount, ...) ... {try {em.getTransaction().begin();BankAccount fromAccount = em.find(BankAccount.class, ...);BankAccount toAccount = em.find(BankAccount.class, ...);fromAccount.debit(amount);toAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)}}public void credit(MonetaryAmount amount, AccountId ...) ... {try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.credit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)}}public void debit(MonetaryAmount amount, AccountId ...) ... {try {em.getTransaction().begin();BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.debit(amount);em.getTransaction().commit();...} catch(Exception e) {em.getTransaction().rollback();// handle exception (possibly rethrowing it)}}
}

我们的代码要简单一些。 由于我们没有创建EntityManager ,所以不必关闭它。 但是我们仍在调用getTransaction().begin() 。 有没有更好的办法? 首先如何将EntityManager注入对象?

从本系列的前一篇文章中 ,精明的读者可能已经在考虑让Spring为我们完成这项工作。 当然是这样!

EntityManager@PersistenceContext

我们告诉Spring通过添加PersistenceAnnotationBeanPostProcessor (通过XML <bean>或通过通过AnnotationConfigApplicationContext加载的@Configuration类使用基于Java的配置)从EntityManagerFactory注入EntityManager

  • 使用基于XML的配置时, PersistenceAnnotationBeanPostProcessor<context:annotation-config />元素透明地激活。 并且<context:component-scan />也透明地激活了此元素。
  • 使用基于Java的@Configuration ,将使用AnnotationConfigApplicationContext 。 并使用它始终注册注释配置处理器(其中之一是上述PersistenceAnnotationBeanPostProcessor )。

通过添加单个bean定义,Spring容器将充当JPA容器,并从EntityManagerFactory注入EnitityManager

JPA和

现在我们有了EntityManager ,如何告诉Spring为我们开始交易?

我们告诉Spring通过将方法标记为@Transactional (或将类标记为@Transactional ,使所有公共方法都具有事务性)来开始事务。 这与Spring通过JDBC启用事务的方式一致。

import javax.persistence.*;
import org.springframework.transaction.annotation.Transactional;@Transactional
... class BankAccountService {@PersistenceContextprivate EntityManager em;...public void transfer(MonetaryAmount amount, ...) ... {BankAccount fromAccount = em.find(BankAccount.class, ...);BankAccount toAccount = em.find(BankAccount.class, ...);fromAccount.debit(amount);toAccount.credit(amount);}public void credit(MonetaryAmount amount, AccountId ...) ... {BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.credit(amount);}public void debit(MonetaryAmount amount, AccountId ...) ... {BankAccount theAccount = em.find(BankAccount.class, ...);theAccount.debit(amount);}
}

哇,真好! 我们的代码短了很多。

就像本系列第一部分中所解释的那样,当Spring遇到此注释时,它将代理该对象(通常称为Spring管理的Bean)。 代理为标记为@Transactional方法启动事务(如果没有正在进行的事务),并在方法成功返回时结束事务。

1f997647

调用debit()将使用事务。 单独调用credit()将使用交易。 但是,当调用transfer()时会发生什么?

由于transfer()方法被标记为@Transactional ,Spring将启动一个事务。 相同的事务将用于对debit()credit()调用。 换句话说, debit(amount)credit(amount)不会启动新交易。 它将使用正在进行的事务(因为有一个事务)。

可是等等! Spring如何知道何时注入适当的实体管理器? 它仅在调用事务方法时才注入吗?

共享的

在我的一个培训课程中 ,我尝试了以下内容,以更好地理解Spring如何通过@PersistenceContext注入EntityManager 。 而且我相信它也会帮助其他人。 因此,这是我尝试的方法:

import javax.persistence.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.InitializingBean;@Transactional
... class BankAccountService implements InitializingBean {@PersistenceContextprivate EntityManager em;...@Overridepublic void afterPropertiesSet() {System.out.println(em.toString());}...
}

应用程序上下文启动后,控制台上将显示类似这样的输出。

Shared EntityManager proxy for target factory [...]

那么,这个共享实体管理器是什么?

当应用程序上下文启动时,Spring注入一个共享实体管理器。 共享EntityManager行为就像从JPA规范定义的从应用程序服务器的JNDI环境中获取的EntityManager一样。 它将所有调用委派给当前的事务性EntityManager (如果有); 否则,它将按操作退回到新创建的EntityManager

回到我们的问题。 Spring没有在正确的时间注入正确的实体管理器。 它总是注入一个共享的实体管理器。 但是,该共享实体管理器是事务感知的。 如果存在正在进行的事务,它将委派给当前的事务性EntityManager

结论

奶油啤酒 本系列分为两部分。 我希望通过从纯文本版本的JDBC和JPA(没有DAO和存储库)开始,我可以使自己更清楚地了解Spring如何在后台管理事务。 而且,通过对Spring的幕后工作有一个更清晰的了解,您可以更好地进行故障排除,了解为什么会得到一个TransactionRequiredException说“没有可用的事务EntityManager”,并为应用程序添加更好的修复程序。

现在,该冷了。

翻译自: https://www.javacodegeeks.com/2016/02/spring-managed-transactions-explained-part-2-jpa.html

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

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

相关文章

CCC数字钥匙设计【BLE】--车主配对之BLE OOB配对

本文主要介绍CCC3.0采用BLE进行车主配对时&#xff0c;关于蓝牙OOB配对的内容。 首先&#xff0c;介绍下BLE Pairing的一些基础知识&#xff0c;有一些基本概念。之后&#xff0c;再着重介绍CCC规范定义的BLE OOB配对流程。 1、BLE Pairing基础知识 下面先简单介绍下BLE 5.0协…

Linux 查看内存状态

# 查看系统内存 命令&#xff1a;free 注&#xff1a;默认k单位显示注&#xff1a;-m 以MB注&#xff1a;-g以GB 单位显示total used free shared buffers cached Mem: 497 463 33 0 13 124 -/ buffe…

Altium Designer导入pcb原件之后都是绿的

转载于:https://www.cnblogs.com/chulin/p/8342041.html

在JConsole和VisualVM中查看DiagnosticCommandMBean

我已经将JConsole用作合适的通用JMX客户端已有很多年了。 该工具通常随Oracle JDK一起提供&#xff0c;并且易于使用。 在JMX交互方面&#xff0c;JConsole优于VisualVM的最大优点是JConsole带有内置的MBeans选项卡&#xff0c;而必须为VisualVM中的相同功能应用插件。 但是&am…

人人商城生成app教程_人人商城APP打包教程(APICLOUD版)

一.APP环境搭建和配置编译1.登录APICLOUD后台新建应用step1 注册账号注册apicloud 账号并登录APICLOUD控制台step2 新建应用再账户下面找到开发控制台>开发控制台>创建应用 填写应用名和说明&#xff0c;必选Native App创建Native App2 .开发工具下载安装APICLOUD开发工具…

WPF快速入门系列(2)——深入解析依赖属性

一、引言 感觉最近都颓废了&#xff0c;好久没有学习写博文了&#xff0c;出于负罪感&#xff0c;今天强烈逼迫自己开始更新WPF系列。尽管最近看到一篇WPF技术是否老矣的文章&#xff0c;但是还是不能阻止我系统学习WPF。今天继续分享WPF中一个最重要的知识点——依赖属性。 二…

圆台下料展开计算方法_怎么画 圆锥台展开图

展开全部1、 画出圆台的主视抄图(等腰梯形)袭&#xff1a;圆台的上bai下底直径为等腰梯形du的上zhi下底&#xff0c;圆台的高为等dao腰梯形的高&#xff1b;2、将等腰梯形补画成等腰三角形&#xff1b;(图中的虚线三角形即为补画部分)&#xff1b;3、以三角形的顶点为圆心&…

.31-浅析webpack源码之doResolve事件流(3)

放个流程图&#xff1a; 这里也放一下request对象内容&#xff0c;这节完事后如下(把vue-cli的package.json也复制过来了)&#xff1a; /*{ context: { issuer: , compiler: undefined },path: d:\\workspace\\doc,request: ./input.js,query: ,module: false,directory: false…

c++ 虚函数,纯虚函数的本质区别

转载博客&#xff1a;https://mp.weixin.qq.com/s?__bizMzAxNzYzMTU0Ng&amp;mid2651289202&amp;idx1&amp;sn431ffd1fae4823366a50b68aed2838d4&amp;chksm80114627b766cf31f72018ef5f1fe29591e9f6f4bd72018e7aea849342ca6f0a271fb38465ae#rd 学习C的多态性&…

云通讯短信验证码实例

1.注册登录云通讯 http://www.yuntongxun.com/user/login 2.创建应用得到应用相关信息 3.下载对应相关的Demo示例  http://www.yuntongxun.com/doc/rest/sms/3_2_2_3.html 4.send.php文件添加代码方便后续操作 session_start(); //随机验证码 $code rand(100000,999999)…

java 数组 内存_图解Java数组的内存分配

1. Java数组是静态的Java是静态语言&#xff0c;所以Java的数组也是静态的&#xff0c;即&#xff1a;数组被初始化后&#xff0c;长度不可变静态初始化&#xff1a;显式指定每个数组元素的初始值&#xff0c;系统决定数组长度String[] books new String[]{"疯狂Java讲义…

libgdx和Kotlin –类[2D平台原型]

这篇文章是libgdx和Kotlin文章的后续文章。 我已经决定开发一个简单的2D平台程序的原型&#xff08;沿着我的早期文章中的Star Assault进行介绍&#xff09;&#xff0c;但是我一直在使用和学习Kotlin&#xff0c;而不是Java。 对于本教程&#xff0c;该项目应处于上一篇文章…

spring jmx_JMX和Spring –第2部分

spring jmx这篇文章从本教程的第1部分继续。 嗨&#xff0c;在我的上一篇文章中&#xff0c;我解释了如何通过Spring设置JMX服务器以及如何通过身份验证和授权保护对它的访问。 在本文中&#xff0c;我将展示如何实现一个简单的MBean&#xff0c;该MBean允许用户在运行时更改L…

LeetCode:位运算实现加法

LeetCode&#xff1a;位运算实现加法 写在前面 位运算符 实现加法的思路 两个加数&#xff0c;比如5(101)和6(110)&#xff0c;如何不用加法就能得出两者之和呢&#xff1f; 我们知道二进制计算中&#xff0c;如果使用异或将会产生无进位的两者之和&#xff0c;而两数相与将会产…

[机器学习] 模型评价参数,准确率,召回率,F1-score

很久很久以前&#xff0c;我还是有个建筑梦的大二少年&#xff0c;有一天&#xff0c;讲图的老师看了眼我的设计图&#xff0c;说&#xff1a;“我觉得你这个设计做得很紧张”&#xff0c;当时我就崩溃&#xff0c;对紧张不紧张这样的评价标准理解无能。多年后我终于明白老师当…

java记录登陆时间_Spring security如何实现记录用户登录时间功能

一、原理分析spring security提供了一个接口 AuthenticationSuccessHandler,该接口中只有一个方法&#xff0c;用来进行登录成功后的操作public interface AuthenticationSuccessHandler {/*** Called when a user has been successfully authenticated.** param request the r…

bzoj3680

$模拟退火$ $这种全局最优的问题用模拟退火$ $模拟退火就是每次向四周随机移动&#xff0c;移动的幅度和温度成正比&#xff0c;如果新的位置更优就接受&#xff0c;否则按一定概率接收&#xff0c;概率和温度成正比$ $最后稳定后再在最优解附近蹦跶几下看看有没有更好的$ $你问…

01背包(修订版)

由于时间比较充裕&#xff0c;重新修订一部分。 这次把一些补充的放进来&#xff0c;其他的基础说明见后半部分 这些一共说明&#xff1a;01背包、完全背包、多重背包 将会详细说明。 三种背包混合、二维背包、分组背包、依赖背包、泛化背包 将大致说明。 01背包 如上次说明一…

java 马踏棋盘优化_我所知道的十大常用算法之马踏棋盘算法(深度搜索、贪心思想优化 )...

前言需求今天我们学习的是马踏棋盘算法&#xff0c;我们还是从一个场景里引入看看马踏棋盘算法也被称为骑士周游问题将马随机放在国际象棋的66棋盘Board0&#xff5e;5的某个方格中提示&#xff1a;马按走棋规则(马走日字)进行移动要求&#xff1a;每个方格只进入一次&#xff…

app engine_App Engine中的Google Services身份验证,第2部分

app engine在本教程的第一部分中&#xff0c; 我介绍了如何使用OAuth进行Google API服务的访问/身份验证。 不幸的是&#xff0c;正如我稍后发现的那样&#xff0c;我使用的方法是OAuth 1.0&#xff0c;显然现在Google正式弃用了OAuth 1.0&#xff0c;改用OAuth 2.0版本。 显然…