JPA 2 | 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的示例。

public class EmployeeServiceImpl implements EmployeeService { @PersistenceContext(unitName="EmployeeService") EntityManager em; public void assignEmployeeToProject(int empId, int projectId) { Project project = em.find(Project.class, projectId); Employee employee = em.find(Employee.class, empId); project.getEmployees().add(employee); employee.getProjects().add(project); }

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

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

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

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

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

交易范围实体管理器

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

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

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

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

public class EmployeeServiceImpl implements EmployeeService { @PersistenceContext(unitName="EmployeeService") EntityManager em; public void assignEmployeeToProject(int empId, int projectId) { Project project = em.find(Project.class, projectId); Employee employee = em.find(Employee.class, empId); project.getEmployees().add(employee); employee.getProjects().add(project); }

在上面的示例中,assignEmployeeToProject方法的第一行正在EntityManager上调用find方法。 调用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实例上工作的业务方法。 让我们将此业务类称为LibraryUserManagementService,它具有业务接口UserManagementService。 LibraryUserManagementService在LibraryUsers实体实例上工作。 图书馆可以将多本书借给LibraryUser。

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

@Stateful 
public class LibraryUserManagementService implements UserManagementService { @PersistenceContext(unitName="UserService") EntityManager em; LibraryUser user; public void init(String userId) { user = em.find(LibraryUser.class, userId); } public void setUserName(String name) { user.setName(name); } public void borrowBookFromLibrary(BookId bookId) { Book book = em.find(Book.class, bookId); user.getBooks().add(book); book.setLendingUser(user); } // ... @Remove public void finished() { } 
}

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

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

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

@Stateful 
public class LibraryUserManagementService implements UserManagementService { @PersistenceContext(unitName="UserService" , type=PersistenceContextType.EXTENDED) EntityManager em;LibraryUser user; public void init(String userId) { user = em.find(LibraryUser.class, userId); } public void setUserName(String name) { user.setName(name); } public void borrowBookFromLibrary(BookId bookId) { Book book = em.find(Book.class, bookId); user.getBooks().add(book); book.setLendingUser(user); } // ... @Remove public void finished() { } 
}

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

应用范围的实体管理器

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

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

EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit"); EntityManager em = emf.createEntityManager();

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

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

EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit" , myProperties); 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:

public class ItemDAOImpl implements ItemDAO { @PersistenceContext(unitName="ItemService") EntityManager em; LoggingService ls; @TransactionAttribute()public void createItem(Item item) { em.persist(item); ls.log(item.getId(), "created item"); } // ... 
}

LoggingService.java:

public class LoggingService implements AuditService { @PersistenceContext(unitName="ItemService") EntityManager em; @TransactionAttribute()public void log(int itemId, String action) { // verify item id is valid if (em.find(Item.class, itemId) == null) { throw new IllegalArgumentException("Unknown item id"); } LogRecord lr = new LogRecord(itemId, action); em.persist(lr); } }

调用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

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

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

相关文章

JavaScript高级特征之面向对象笔记

Javascript面向对象:函数: * Arguments对象: * Arguments对象是数组对象 * Arguments对象的length属性可以获取参数的个数 * 利用Arguments对象模拟函数的重载效果(javascript中不存在函…

Object.prototype.hasOwnProperty与Object.getOwnPropertyNames

Object.prototype.hasOwnProperty() 所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。 使用 hasOwnProperty 方法判断属自身…

IE9真的支持CSS3和HTML5?

微软昨天在其2009年专业开发者大会上展示 了下一个版本的Internet Explorer浏览器IE9。 尽管只是一个早期版本,IE开发团队还是比较高调的宣布了IE9的一些改 进,比如速度比之前的IE版本都更快些,缩小与FF和webkit之间的差距(也就是还是赶不上了…

绘图用计算机软件的基本种类有,主编教您电脑绘图软件有哪些

绘图软件是指专业人员根据一定准则设计的用于计算机绘图软件程序,种类非常多,通过它们可以满足足广大用户的基本绘图要求。下面,我就给大家介绍电脑绘图软件有哪些。电脑是根据指令进行高速计算的电子设备,功能强大,时…

python安装math库_Python-math库

导入函数import mathe#表示一个常量>>> math.e2.718281828459045exp#返回math.e,也就是2.71828的x次方exp(x)>>> math.exp(1)2.718281828459045>>> math.exp(2)7.38905609893065>>> math.exp(3)20.085536923187668pi#数字常量,…

使用Encog,ROME,JSoup和Google Guava进行博客分类

继续使用Programming Collection Intelligence (PCI),下一个练习是使用距离得分根据相关博客中使用的单词确定博客列表。 我已经找到Encog作为AI /机器学习算法的框架,为此,我需要一个RSS阅读器和一个HTML解析器。 我…

HTML浮动导致高度塌陷,HTML 文档流,设置元素浮动,导致父元素高度无法自适应的解决方法(高度欺骗)...

元素浮动定义float 属性定义元素在哪个方向浮动。以往这个属性总应用于图像,使文本围绕在图像周围,不过在 CSS 中,任何元素都可以浮动。浮动元素会生成一个块级框,而不论它本身是何种元素。如果浮动非替换元素,则要指定…

Python API简单验证

前言 因为CMDB内部的需求,需要一个API进行数据传输,用来传递需要抓取的服务端信息信息给抓取的autoclient,autoclient抓取好之后再通过API传输到服务器,保存到数据库。但是为了防止恶意的API访问,需要做一个验证。 设想…

完全CSS实现鼠标移上出现层的效果(超简单)

看过许多鼠标事件,都很复杂,太多的文件和繁杂的代码,而且好多都是用js实现,加载速度很慢。 这几天一直在找一种简单的实现效果,完全 CSS编写的效果,现在找到了,非常的少。 这就是完全 CSS实现的层效果&am…

搜索引擎学习日志

了解是什么:Google的咖啡因系统、Megastore云存储系统、Pregel云图计算模型、暗网爬取技术、Web2.0网页作弊、机器学习排序、情景搜索、社会化搜索 学习思想:先全局、再细节 《这就是搜索引擎:核心技术详解》page 33 / 315 开始第二章&#x…

前端微信签名验证工具_微信小程序API 用户数据的签名验证和加解密

用户数据的签名验证和加解密数据签名校验为了确保 开放接口 返回用户数据的安全性,微信会对明文数据进行签名。开发者可以根据业务需要对数据包进行签名校验,确保数据的完整性。签名校验算法涉及用户的session_key,通过 wx.login 登录流程获取…

会计电算化的过程 实质上是用计算机,会计电算化的过程,实质上是用计算机()的过程。A.单一地替代手工会计操作B.单一地替代对会计进行分...

会计电算化的过程,实质上是用计算机()的过程。A.单一地替代手工会计操作B.单一地替代对会计进行分更多相关问题以下对冷饮操作要求描述错误的是:()客舱网路的功用。()次高速减脂过程中一般每减多少做一个平台过渡()架线施工时弧垂…

Spring MVC控制器的单元测试:配置

传统上,为Spring MVC控制器编写单元测试既简单又成问题。 尽管编写调用控制器方法的单元测试非常简单,但问题是这些单元测试不够全面。 例如,我们不能仅通过调用已测试的控制器方法来测试控制器映射,验证和异常处理。 Spring MVC…

css实现鼠标覆盖显示大图

html <div <a href”#”> <img src”img01.jpg”> <img src”img02.jpg”> </a> </div> css img{border:none;} .pic{position:relative;top:10px;left:10px} .pic a .large{position:absolute;height:0;width:0;} .pic a:hover{di…

前端js编码

1、首先是encodeURI和encodeURIComponent&#xff1b; 从名字可以清晰的看出他两都是主要用于url编码的&#xff0c;那之间有什么区别呢&#xff1f;唯一区别就是编码的字符范围&#xff0c;其中 encodeURI方法不会对下列字符编码 ASCII字母、数字、~!#$&*():/,;?&#x…

common lisp的几个基本概念

S-表达式 quote nil 与 () cons car cdr 真假 predicate 谓词与 t 与 nil null 函数 与 not 函数 if then else and 与 or defun recursion 递归 谓词 eql 与 equal format 与 read&#xff1a;format 在函数体内调用不会输出 nil&#xff08;format 函数本身有返回值为 nil) l…

python循环结束执行后面代码_计算机程序中某种代码的反复执行,称为________。Python中的循环有重复一定次数的________,也有重复到某种情况结束的________。...

3.(2019高一下浙江期末)数制转换。将一个K进制(k<10)数x转换成十进制数可采用如下方法&#xff1a;主要方法是从右向左&#xff0c;依次取数x的各位数字&#xff0c;分别计算出该数从右边数起的第i位数字与k(i-1)的积&#xff0c;再将其累加&#xff0c;直到所有的数字取完为…

计算机流体力学软件基础及工程应用,流体力学及其工程应用(英文版·原书第10版)2013年版...

流体力学及其工程应用(英文版原书第10版)出版时间&#xff1a;2013年版内容简介《流体力学及其工程应用(英文版原书第10版)/时代教育国外高校优秀教材精选》继承并发扬了前9版讲述流体力学物理现象的传统&#xff0c;并以最简单而且尽可能是最清晰&#xff0c;但又不使用复杂数…

Spring MVC测试框架入门–第2部分

这个迷你系列的第一个博客介绍了Spring MVC测试框架&#xff0c;并展示了其在单元测试Spring MVC Controller类中作为控制器而不是POJO进行单元测试的用途。 现在是时候讨论使用框架进行集成测试了。 “集成测试”是指将Spring上下文加载到测试环境中&#xff0c;以便控制器可…

jira java接口生成问题

参考页面: 可方便扩展的JIRA Rest Web API的封装调用 JIRA是一个缺陷跟踪管理系统&#xff0c;被广泛应用于缺陷跟踪、客户服务、需求收集、流程审批、任务跟踪、项目跟踪和敏捷管理等工作领域,当我们需要把第三方业务系统集成进来时&#xff0c;可以调用他的API。 JIRA本身的A…