JPA EntityManagers,事务及其周围的一切

介绍

对我来说,最令人困惑和不清楚的事情之一是,作为Java开发人员,一直是围绕事务管理的谜团,尤其是JPA如何处理事务管理。 事务什么时候开始,什么时候结束,实体的持久化方式,持久性上下文等等。 诸如Spring之类的框架也无助于理解概念,因为它们提供了另一层抽象,这使事情难以理解。 在今天的帖子中,我将尝试揭露JPA关于实体管理的规范,其事务规范以及如何更好地理解该概念如何帮助我们有效地设计和编码的某些秘密。 我们将努力保持讨论 

尽管我们将同时研究Java SE(其中没有Java EE容器)和基于Java EE的示例。

基本概念

在深入探讨更多细节之前,让我们快速完成一些基础课程及其在JPA中的含义。

  1. EntityManager –一个管理实体的持久状态(或生命周期)的类。
  2. 持久性单元 –是实体类的命名配置。
  3. 持久性上下文 –是一组托管的实体实例。 实体类是持久性单元配置的一部分。
  4. 托管实体 –如果实体实例是持久性上下文的一部分,并且该实体管理器可以对其执行操作,则该实体实例将受到管理。

从上面的第一点和第三点,我们可以推断出实体管理器总是管理持久性上下文。 因此,如果我们了解持久性上下文,我们将了解EntityManager。

细节

JPA中的EntityManager

JPA中定义了EntityManager的三种主要类型。

  • 容器管理和交易范围的实体管理器
  • 容器管理和扩展范围实体管理器
  • 应用程序管理的实体管理器

现在,我们将更详细地介绍其中的每一个。

容器管理的实体管理器

当应用程序的一个容器(例如Java EE容器或任何其他自定义容器,例如Spring)管理实体管理器的生命周期时,该实体管理器被称为“容器管理”。 获取容器管理的EntityManager的最常见方法是在EntityManager属性上使用@PersistenceContext批注。 这是定义EntityManager的示例。

 
  1. public class EmployeeServiceImpl implements EmployeeService {

  2. @PersistenceContext(unitName="EmployeeService")

  3. EntityManager em;

  4. public void assignEmployeeToProject(int empId, int projectId) {

  5. Project project = em.find(Project.class, projectId);

  6. Employee employee = em.find(Employee.class, empId);

  7. project.getEmployees().add(employee);

  8. employee.getProjects().add(project);

  9. }

在上面的示例中,我们在EntityManager类型实例变量上使用了@PersistenceContext批注。 PersistenceContext批注具有属性“ unitName”,用于标识该上下文的持久性单元。

容器管理的实体管理器有两种形式:

  1. 交易范围的实体管理器
  2. 扩展范围实体管理器

请注意,上述范围实际上是指实体管理器管理的持久性上下文的范围。 它不是EntityManager本身的范围。

让我们依次查看它们中的每一个。

交易范围实体管理器

这是应用程序中最常用的实体管理器。 同样在上面的示例中,我们实际上是在创建事务作用域实体管理器。 每当解析由@PersistenceContext创建的引用时,都会返回事务作用域实体管理器。

使用事务作用域实体管理器的最大好处是它是无状态的。 这也使事务范围的EntityManager线程安全,因此实际上无需维护。 但是我们只是说EntityManager管理实体的持久状态,而实体的持久状态是注入EntityManager的持久性上下文的一部分。 那么,上述关于无国籍状态的说法又如何呢?

答案在于所有容器管理的实体管理器都依赖于JTA事务。 每次在实体管理器上调用操作时,容器代理(容器在实例化时在实体管理器周围创建一个代理)都会检查JTA事务上是否存在任何持久性上下文。 如果找到一个,则实体管理器将使用此持久性上下文。 如果找不到,则将创建一个新的持久性上下文并将其与事务关联。

让我们以上面讨论的相同示例来了解实体管理器和事务创建的概念。

 
  1. public class EmployeeServiceImpl implements EmployeeService {

  2. @PersistenceContext(unitName="EmployeeService")

  3. EntityManager em;

  4. public void assignEmployeeToProject(int empId, int projectId) {

  5. Project project = em.find(Project.class, projectId);

  6. Employee employee = em.find(Employee.class, empId);

  7. project.getEmployees().add(employee);

  8. employee.getProjects().add(project);

  9. }

在上面的示例中,assignEmployeeToProject方法的第一行正在EntityManager上调用find方法。 查找调用将强制容器检查现有交易。 是否存在事务(例如,对于Java EE中的无状态会话Bean,容器在每次调用Bean上的方法时都保证事务可用)或不存在。 如果事务不存在,它将抛出异常。 如果存在,它将检查持久性上下文是否存在。 自从第一次调用EntityManager的任何方法以来,持久性上下文尚不可用。 然后,实体管理器将创建一个并使用它来查找项目bean实例。

在下一个查找调用中,实体管理器已经具有关联的事务以及与其关联的持久性上下文。 它使用相同的事务来查找员工实例。 在方法的第二行结束时,将同时管理项目和员工实例。 在方法调用结束时,将提交事务,并保留人员和员工的托管实例。 要记住的另一件事是,当事务结束时,持久性上下文消失了。

扩展范围实体管理器

如果并且当您希望持久性上下文在方法范围之外可用时,请使用具有扩展范围的实体管理器。 理解扩展范围实体管理器的最好方法是以一个类为例,该类需要维护某种状态(该状态是由于诸如myEntityManager.find(“ employeeId”)之类的事务请求而创建的,然后使用该雇员),并且通过各种业务方法共享状态。

因为Persistence Context在方法调用之间共享并且用于维护状态,所以除非您在有状态会话Bean中使用它们(容器负责使其成为线程安全的),否则它通常不是线程安全的。 重申一下,如果您使用的是Java EE容器,则将在Stateful Session Bean(带有@Stateful注释的类)内使用扩展范围实体管理器。 如果您决定在有状态bean之外使用它,则该容器不能保证您可以安全地执行线程操作,而必须自己处理。 如果您使用的是像Spring这样的第三方容器,情况也是如此。

让我们看一下使用状态会话Bean时Java EE环境中扩展作用域实体管理器的示例。

该示例中的目标是创建一个业务类,该业务类具有在LibraryUser Entity实例上工作的业务方法。 让我们将此具有业务接口UserManagementService的业务类LibraryUserManagementService命名。 LibraryUserManagementService在LibraryUsers实体实例上工作。 图书馆可以将多本书借给LibraryUser。

这是描述上述情况的有状态会话Bean的示例。

 
  1. @Stateful

  2. public class LibraryUserManagementService implements UserManagementService {

  3. @PersistenceContext(unitName="UserService")

  4. EntityManager em;

  5. LibraryUser user;

  6. public void init(String userId) {

  7. user = em.find(LibraryUser.class, userId);

  8. }

  9. public void setUserName(String name) {

  10. user.setName(name);

  11. }

  12. public void borrowBookFromLibrary(BookId bookId) {

  13. Book book = em.find(Book.class, bookId);

  14. user.getBooks().add(book);

  15. book.setLendingUser(user);

  16. }

  17. // ...

  18. @Remove

  19. public void finished() {

  20. }

  21. }

在上面使用用户实例的情况下,更自然的是先获得一个实例,然后逐步进行操作,只有完成后,我们才应保留用户实例。 但是,问题在于实体管理器是事务范围的。 这意味着init将在其自身的事务中运行(因此具有其自身的持久性上下文),而roweBookFromLibrary将在其自身的事务中运行。 结果,init方法一结束,用户对象就变得不受管理。

为了确切地解决此类问题,我们使用PersistenceContextType.EXTENDED类型的实体管理器。

这是带有PersistenceContextType EXTENDED的修改后的示例,可以很好地工作。

 
  1. @Stateful

  2. public class LibraryUserManagementService implements UserManagementService {

  3. @PersistenceContext(unitName="UserService" , type=PersistenceContextType.EXTENDED)

  4. EntityManager em;

  5. LibraryUser user;

  6. public void init(String userId) {

  7. user = em.find(LibraryUser.class, userId);

  8. }

  9. public void setUserName(String name) {

  10. user.setName(name);

  11. }

  12. public void borrowBookFromLibrary(BookId bookId) {

  13. Book book = em.find(Book.class, bookId);

  14. user.getBooks().add(book);

  15. book.setLendingUser(user);

  16. }

  17. // ...

  18. @Remove

  19. public void finished() {

  20. }

  21. }

在上述场景中,用于管理用户实例的PersistenceContext是由Java EE容器在Bean初始化时创建的,并且在调用完成的方法(在该时间提交事务)之前可用。

应用范围的实体管理器

不是由容器而是由应用程序本身创建的实体管理器是应用程序范围的实体管理器。 为了使定义更清晰,每当我们通过在EntityManagerFactory实例上调用createEntityManager来创建实体管理器时,实际上是在创建应用程序范围的实体管理器。 所有基于Java SE的应用程序实际上都使用应用程序范围的实体管理器。 JPA为我们提供了一个Persistence类,该类用于最终创建应用程序范围的实体管理器。

以下是如何创建应用程序范围的EM的示例:

 
  1. EntityManagerFactory emf =

  2. Persistence.createEntityManagerFactory("myPersistenceUnit");

  3. EntityManager em = emf.createEntityManager();

请注意,要创建应用程序范围的EntityManager,在应用程序的META-INF文件夹中需要有一个persistence.xml文件。

EntityManager可以通过两种方式创建。 上面已经显示了一个。 创建EntityManager的另一种方法是将一组属性作为参数传递给 
createEntityManagerFactory方法。

 
  1. EntityManagerFactory emf =

  2. Persistence.createEntityManagerFactory("myPersistenceUnit" , myProperties);

  3. EntityManager em = emf.createEntityManager();

如果要创建自己的应用程序托管实体管理器,请确保在每次使用完它后都将其关闭。 这是必需的,因为您现在正在管理应如何以及何时创建和使用EntityManager。

交易管理

交易与实体直接相关。 实质上,管理事务意味着要管理实体生命周期(创建,更新,删除)的管理方式。 理解事务管理的另一个关键是要了解持久性上下文如何与事务交互。 值得注意的是,从最终用户的角度来看,即使我们使用EntityManager的实例,EntityManager的唯一作用是确定持久性上下文的生存期。 它在决定持久化上下文应如何表现方面没有任何作用。 重申一下,持久性上下文是一组实体实例的托管集合。 每当事务开始时,Persistence Context实例都会与之关联。 当事务结束时(例如,提交),持久性上下文将被刷新并与事务解除关联。

JPA支持两种类型的事务管理类型。

  • 资源本地交易
  • JTA或全球交易

资源本地事务是指JDBC驱动程序的本机事务,而JTA事务是指JEE服务器的事务。 资源本地事务涉及单个事务资源,例如JDBC连接。 每当在单个事务中需要两个或更多资源(例如JMS连接和JDBC连接)时,都可以使用JTA事务。 
容器管理的实体管理器始终使用JTA事务,因为容器负责事务生命周期管理并在多个事务资源中产生事务。 应用程序管理的实体管理器可以使用资源本地事务或JTA事务。

通常,在JTA或全局事务中,第三方事务监控器会在事务中获取不同的事务资源,为提交做准备,最后提交事务。 首先准备事务资源(通过空运行)然后提交(或回滚)的过程称为两阶段提交。

有关XA协议的附带说明 –在全球交易中,交易监视器必须不断与不同的交易资源进行对话。 不同的交易资源会说不同的语言,因此交易监视器可能无法理解。 XA是一个协议规范,为事务监视器与不同的事务资源进行交互提供了通用基础。 JTA是说XA的全球事务监控器规范,因此能够管理多个事务资源。 兼容Java EE的服务器具有内置的JTA实现。其他容器(例如Spring)可以自己编写或使用其他实现(例如Java Open Transaction Manager,JBoss TS等)来支持JTA或全局事务。

持久性上下文,事务和实体管理器

持久性上下文可以与单个或多个事务关联,也可以与多个实体管理器关联。 持久性上下文已向事务注册,以便在提交事务时可以刷新持久性上下文。 事务开始时,实体管理器将查找活动的持久性上下文实例。 如果不可用,它将创建一个并将其绑定到事务。 通常,持久性上下文的范围与事务紧密相关。 当事务结束时,与该事务关联的持久性上下文实例也结束。 但是有时,在大多数情况下,在Java EE世界中,我们需要事务传播,这是在单个事务中的不同实体管理器之间共享单个持久性上下文的过程。

持久性上下文可以有两个范围:

  • 事务范围的持久性上下文
  • 扩展范围的持久性上下文

我们已经讨论了事务/扩展范围的实体管理器,并且我们也知道实体管理器可以是事务或扩展范围的。 关系不是偶然的。 事务范围的实体管理器创建事务范围的持久性上下文。 扩展范围实体管理器使用扩展持久性上下文。 扩展持久性上下文的生命周期与Java EE环境中的有状态会话Bean相关联。

让我们简要地讨论这些持久性上下文

事务范围的持久性上下文

TSPC仅在需要时由实体管理器创建。 仅当首次调用实体管理器上的方法时,事务作用域实体管理器才创建TSPC。 因此,持久性上下文的创建是懒惰的。 如果已经存在传播的持久性上下文,则实体管理器将使用该持久性上下文。

了解持久性上下文传播对于识别和调试代码中与事务相关的问题非常重要。 让我们看一个如何传播事务范围的持久性上下文的示例。

ItemDAOImpl.java:

 
  1. public class ItemDAOImpl implements ItemDAO {

  2. @PersistenceContext(unitName="ItemService")

  3. EntityManager em;

  4. LoggingService ls;

  5. @TransactionAttribute()

  6. public void createItem(Item item) {

  7. em.persist(item);

  8. ls.log(item.getId(), "created item");

  9. }

  10. // ...

  11. }

LoggingService.java:

 
  1. public class LoggingService implements AuditService {

  2. @PersistenceContext(unitName="ItemService")

  3. EntityManager em;

  4. @TransactionAttribute()

  5. public void log(int itemId, String action) {

  6. // verify item id is valid

  7. if (em.find(Item.class, itemId) == null) {

  8. throw new IllegalArgumentException("Unknown item id");

  9. }

  10. LogRecord lr = new LogRecord(itemId, action);

  11. em.persist(lr);

  12. }

  13. }

调用ItemDAOImpl的createItem方法时,将在实体管理器实例上调用persist方法。 假设这是对实体管理器方法的第一次调用。 实体管理器将查找单元名称为“ ItemService”的任何传播的持久性上下文。 它找不到一个,因为这是对实体管理器的第一个调用。 因此,它创建了一个新的持久性上下文实例并将其附加到自身。 然后,它继续保存Item对象。 持久化项目对象后,我们将调用以记录刚刚持久化的项目信息。 请注意,LoggingService有其自己的EnitityManager实例,方法日志中具有注释@TransactionAttribute(如果在Java EE envt中并且将bean声明为EJB,则不需要此注释)。 
由于TransactionAttribute的默认TransactionAttributeType为REQUIRED,因此LoggingService中的实体管理器将查找以前的事务中可能可用的任何持久性上下文。 它找到一个在ItemDAOImpl的createItem方法内部创建的对象,并使用相同的对象。 这就是为什么即使实际项目尚未持久到数据库(因为尚未提交事务),LoggingService中的实体管理器仍能够找到它,因为持久性上下文已从ItemDAOImpl传播到LoggingService 。

扩展持久性上下文

事务范围持久性上下文是为每个事务创建的(在不传播的情况下),而扩展持久性上下文仅创建一次,并由管理扩展持久性上下文生命周期的类范围内的所有事务使用。 对于Java EE,由状态会话Bean管理扩展的持久性上下文的生命周期。 有状态会话bean的创建是EAGER。 如果是容器托管事务,则在类上的方法被调用后立即创建它。 对于应用程序管理的事务,将在调用userTransaction.begin()时创建。

摘要

在这篇博客文章中,已经讨论了很多东西,实体管理器,事务管理,持久性上下文,所有这些东西如何相互作用以及如何相互配合。

我们讨论了容器管理的实体管理器和应用程序管理的实体管理器,事务范围和扩展范围持久性上下文,事务传播之间的区别。 该博客的大部分材料是阅读了精彩的书: Pro JPA 2的结果 。 如果您想更深入地了解JPA的工作原理,我建议您阅读它。 

参考: JPA 2 | JavaWorld博客博客中的JCG合作伙伴 Anuj Kumar提供了EntityManagers,Transactions及其周围的一切 。

翻译自: https://www.javacodegeeks.com/2013/06/jpa-2-entitymanagers-transactions-and-everything-around-it.html

jpa 事务嵌套事务

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

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

相关文章

哪些是Linux内核的同步机制,Linux内核的同步机制(1)

Linux内核的同步机制(1)yanqin | 2009-04-16 14:51:09 阅读:791发布文章一、 引言%A%A 在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤…

量子机器学习入门科普:解读量子力学和机器学习的共生关系

原作:Reena Shaw安妮 编译自 KDnuggets量子位 出品 | 公众号 QbitAI量子机器学习(Quantum ML)是量子力学和机器学习的一门交叉学科。两者间像一种共生关系,我们可以利用量子计算的力量生成机器学习算法的量子版本,并应…

linux 禁止 密码 登陆,CentOS设置证书登录并禁止密码登录

CentOS设置证书登录并禁止密码登录普通用户登录时,以往的做法往往是使用账号密码登录,但是这样的登录方式风险相当高,使用密钥登录能大大降低风险1. 生成密钥ssh 公钥认证是ssh认证的方式之一。通过公钥认证可实现ssh免密码登陆,s…

RedissonClient 缓存Bug

引入背景 期望RedissonClient 能定期更新&#xff0c;避免网络不稳定导致 Redis 连接失效&#xff0c;只能重启服务&#xff1f; 使用了Caffeine 缓存&#xff0c;自动 2 小时失效剔除RedissonClient /*** 本地缓存.*/private static final Cache<String, RedissonClient&g…

人工智能重构下的金融场景

来源&#xff1a;亿欧概要&#xff1a;从金融领域来看&#xff0c;国际银行业对人工智能的主要应用集中在资本运营、市场分析、客户营销、风险监管等方面。智能客服、智能投顾、智能量化交易……人工智能在金融领域存在巨大的发展空间&#xff0c;但其也使监管更加复杂。近年来…

linux中写脚本不能写中文,Linux系统中Sublime Text无法输入中文怎么办?

Sublime Text是一个代码编辑器&#xff0c;拥有强大的功能&#xff0c;但Sublime Text 在Linux下运行的时候存在无法输入中文的问题&#xff0c;遇到该问题该如何解决呢&#xff1f;下面小编就给大家介绍下Linux下Sublime Text 无法输入中文的解决方法。1.保存下面的代码为subl…

JMM设计原理之双重检查Lock

错误示例 public class DoubleCheckedLocking {/*** 单例.*/private static Instance instance;/*** 获取单例.* return 对象.*/public static Instance getInstance() {if (instance null) {synchronized (DoubleCheckedLocking.class) {if (instance null) {instance new…

深度学习引擎的终极形态是什么?

来源&#xff1a;微软研究院AI头条概要&#xff1a;1月17日&#xff0c;院友袁进辉博士回到微软亚洲研究院做了题为《打造最强深度学习引擎》的报告&#xff0c;分享了深度学习框架方面的技术进展。1月17日&#xff0c;院友袁进辉博士回到微软亚洲研究院做了题为《打造最强深度…

linux进程映像由哪些构成,Linux编程开发进程映像类型分析

进程与线程问题是程序员在学习编程开发语言需要重点掌握的编程知识之一&#xff0c;而今天我们就一起来了解一下&#xff0c;Linux编程开发中进程映像的执行标准。什么是进程映像呢?进程映像是执行程序时所需要的可执行文件&#xff0c;通常会包括下面这些东西代码段(codesegm…

JMM同步原语之final域的内存语义

final 域为引用类型 public class FinalReference {/*** final 域.*/final int[] arr;static FinalReference ref;public FinalReference() {arr new int[1];arr[0] 1;}public static void writeOne() {ref new FinalReference();}public static void writeTwo() {ref.arr[…

AI芯片之争白热化的当下,如何设计一款真正适用于终端的AI芯片?

来源&#xff1a;36氪概要&#xff1a;2017年&#xff0c;人工智能最火的风口一定是AI芯片。2017年&#xff0c;人工智能最火的风口一定是AI芯片。AI芯片的出现&#xff0c;与深度学习技术的成熟及应用密不可分。深度学习的过程可以简化理解为利用大量标注的数据进行训练&#…

linux nslookup命令安装,在CentOS中安装nslookup命令

域名查询工具nslookup并不是Win系统的专利&#xff0c;Linux系统中也可以使用&#xff0c;不过要安装&#xff0c;默认没有。在CentOS中安装nslookup命令:$ sudo yum install bind-utils这个bind-utils包&#xff0c;就包含了nslookup命令。Ubuntu系统应该也是一样的&#xff0…

李开复:AI巨头是有史以来最难以打破的垄断

来源&#xff1a;凤凰网概要&#xff1a;李开复认为目前最需要的是小的AI公司&#xff0c;甚至于打破巨头垄断局面的“破局者”也会是这些由小变大的AI公司。当地时间周二(1月23日)&#xff0c;世界经济论坛在瑞士达沃斯小镇正式举行&#xff0c;各国政要、企业家、学者云集。创…

杨立昆辞Facebook人工智能实验室主任,任首席科学家

来源&#xff1a;澎湃新闻概要&#xff1a;他将不再担任Facebook 人工智能实验室主任一职&#xff0c;改任Facebook首席人工智能科学家&#xff0c;从而能更加专注于带领科学研究与AI策略。当地时间1月23日&#xff0c;执掌Facebook人工智能实验室&#xff08;FAIR&#xff09;…

Redis系列一、redis介绍与安装

一、Redis介绍 redis是一种基于键值对&#xff08;key-value&#xff09;数据库&#xff0c;其中value可以为string、hash、list、set、sorted set等多种数据结构&#xff0c;可以满足很多应用场景。还提供了键过期&#xff0c;发布订阅&#xff0c;事务&#xff0c;流水线&am…

redhat6.3的linux内核版本,1-6-RHEL6.3-内核升级(Red Hat Enterprise Linux Server6.3)@树袋飘零...

本节介绍内容&#xff1a;1、内核的概述2、源码编译安装文件系统中的ntfs内核模块案例分析1、内核的概述Linux操作系统是用来跟硬件和用户程序互联的支撑平台&#xff0c;设备的驱动程序完全可以访问硬件&#xff0c;而设备的驱动程序以模块化的形式设置&#xff0c;可以进行安…

MIT人工突触芯片新突破:指甲大小的芯片有望媲美超算

原作 Jennifer ChuRoot 编译自 MIT News量子位 出品 | 公众号 QbitAI论传递信息的能力&#xff0c;计算机的二进制目前还比不上人脑。因为大脑神经元之间传递的信号形式远多过0或1两种&#xff1a;根据突触&#xff08;神经元之间的结构&#xff09;间不同的神经递质&#xff0…

Redis系列二、redis的五种数据结构和相关指令之String

redis是一种基于键值对&#xff08;key-value&#xff09;的内存数据库&#xff0c;redis数据结构可以分为string、hash、list、set、sorted set。本节中将介绍Redis支持的主要数据结构中的string&#xff0c;以及相关的常用Redis命令。 redis的五种数据结构和相关指令之Strin…

体积最小桌面linux,Tiny Core Linux - 体积最小的精简 Linux 操作系统发行版之一 (仅10多MB) - 蓝月网络...

Tiny Core Linux (TCL) 是一款极体积极小且高度可扩展的微型 Linux 发行版&#xff0c;它将一个 Linux 操作系统精简到仅有 10 多 MB 左右的大小&#xff0c;似乎小巧得有点让人叹为观止&#xff01;要知道无论是常见的 Ubuntu、CentOS、Debian 的体积动辄就是几百MB甚至要上GB…

美国公布长达35页的《2016-2045年新兴科技趋势报告》

来源&#xff1a;云谷资本概要&#xff1a;前段时间美国公布的一份长达35页的《2016-2045年新兴科技趋势报告》。该报告是在美国过去五年内由政府机构、咨询机构、智囊团、科研机构等发表的32份科技趋势相关研究调查报告的基础上提炼形成的。前段时间美国公布的一份长达35页的《…