如何高效使用 EntityManager?
How to use EntityManager efficiently?
最近看了很多关于Java持久化的书籍和文章,越读越迷惑如何正确使用JPA和高效.
现在我有简单的服务层用于每种类型的实体,它负责持久化、删除、搜索等。它从 singleton util class 获取 EntityManagerFactory,creates EntityManager,执行事务,然后关闭 EntityManager。 EMF 仅在应用程序关闭时关闭。如果我理解正确,通过关闭 EntityManager,我所有的对象(与之相关)都变得分离。通过更改对象中的任何值,它们不会被持久化,所以我认为是这种情况。
在我读过的一本书中,我应该使服务 classes @Stateless beans 和 inject 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()
。
最近看了很多关于Java持久化的书籍和文章,越读越迷惑如何正确使用JPA和高效.
现在我有简单的服务层用于每种类型的实体,它负责持久化、删除、搜索等。它从 singleton util class 获取 EntityManagerFactory,creates EntityManager,执行事务,然后关闭 EntityManager。 EMF 仅在应用程序关闭时关闭。如果我理解正确,通过关闭 EntityManager,我所有的对象(与之相关)都变得分离。通过更改对象中的任何值,它们不会被持久化,所以我认为是这种情况。
在我读过的一本书中,我应该使服务 classes @Stateless beans 和 inject 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()
。