如何高效使用 EntityManager?

How to use EntityManager efficiently?

最近看了很多关于Java持久化的书籍和文章,越读越迷惑如何正确使用JPA高效.

现在我有简单的服务层用于每种类型的实体,它负责持久化、删除、搜索等。它从 singleton util class 获取 EntityManagerFactorycreates EntityManager执行事务,然后关闭 EntityManager。 EMF 仅在应用程序关闭时关闭。如果我理解正确,通过关闭 EntityManager我所有的对象(与之相关)都变得分离。通过更改对象中的任何值,它们不会被持久化,所以我认为是这种情况。

在我读过的一本书中,我应该使服务 classes @Stateless beansinject EntityManager 作为 @PersistenceContext。这样一来,我将拥有与我拥有的服务 class 数量相同的 EntityManagers (或者它足够聪明,只注入一个?)。我假设可能会出现这样的情况,即一个对象由多个 EntityManagers 处理,效率低下,可能会导致错误。我能否使 EntityManager 成为单例,它拥有和管理所有内容,并像 EMF 一样贯穿整个应用程序生命周期?

据我所知,EntityManager 应该在每次交易后关闭,但后来我对托管部分感到困惑:

就我而言:

Car car = CarService.findOneById(somelong); //this method returns an already detached object, nothing is ever in managed state
car.setColor("yellow");
CarService.update(car); //updates the db

(这对我来说还不是问题,只是一个不必要的坏处,直到 LAZY 抓取发挥作用。)

我认为它应该如何工作:

Car car = CarService.findOneById(somelong); //does not close the EM
car.setColor("yellow"); //by this call the EM detects(?) and persists the change

实现该目标的方法有哪些,哪些被认为是最佳做法?如果你能给我一个每一层的例子,我将不胜感激。

提前致谢!

P.S.: 我知道这个问题很宽泛,但我希望有人能把我脑子里的问号擦掉。

我不确定这是您期望的完整答案,但只是一些提示:

创建EMF是一个硬操作,而创建EM是一个轻量级的操作。所以是的,推荐EMF是单例的,它是线程安全的。

EM 不是线程安全的。你是对的,建议在交易完成后关闭 EM 以释放你不再需要的实体和资源,但交易并不一定仅限于像你的示例中那样通过 id 获取对象。您可以随意获取一个对象并按照您的意愿进行处理(例如:像您的示例中那样设置 collor)然后关闭交易 - 只需在特殊的 updateCarColor 服务方法中进行即可。

由于您处于 Java EE 环境中,您宁愿将 EM 管理留给容器,当您在同一事务中时,当您使用 [=11= 注入时,容器将共享相同的 EM ].它是由代理完成的——不是真正的 EM 被注入 EntityManager em 字段,而是代理。该代理会将您的呼叫传递给共享 EM。如果没有活动事务,它将创建一个新的共享 EM,然后将调用传递给它。

In a book I've read that I should make the service classes @Stateless beans, and inject the EntityManager as @PersistenceContext in them. That way I'll have the same amount of EntityManagers as many service classes I have

没有。您将获得一个绑定到当前事务的持久性上下文。简而言之,容器会自己做你正在做的事情,除了如果服务 A 调用服务 B,服务 B 调用服务 C,服务 C 调用服务 D,并且它们都共享相同的事务上下文,它们也会获得相同的持久性语境。并且持久化上下文会在事务关闭时关闭。

Car car = CarService.findOneById(somelong); //does not close the EM
car.setColor("yellow"); //by this call the EM detects(?) and persists the change

这将自动使颜色变化持久化,只要您在事务内部而不是外部执行此操作即可。

根据您的描述,我了解到您的服务并不是真正的服务。它们只是 DAO:它们坚持并找到单一类型的实体,并且不包含任何业务逻辑。那不是应该处理交易的地方。事务应该在实际的业务服务层中处理,该层实现应用程序的事务用例。

例如,假设您开发了一个银行应用程序。一个典型的用例是不减少账户余额。或者增加账户余额。或者增加银行自己的账户余额。一个典型的用例是在账户之间进行资金转移。这意味着单笔交易必须:

  • 找到发射器帐户
  • 找到收件人账户
  • 检查转账是否可以完成
  • 计算银行从转账中赚取的百分比
  • 减少发射器账户余额
  • 将赚取的金额添加到银行账户
  • 增加收款人账户余额
  • 将转账操作添加到发射器账户
  • 将转账操作添加到收款账户

此用例将使用多个 DAO:AccountDAO、OperationDAO、BankAccountDAO 等,但所有操作都应在单个事务中完成。整个事务将使用一个独特的持久性上下文,其中管理所有实体,因此所有更改都会自动持久化,而无需在任何 DAO 上调用 update()