2016-08-22 77 views
1

我读过很多关于Java持久性的书籍和文章,而且我越读越容易混淆我关于如何正确使用JPA高效如何高效地使用EntityManager?

现在我有简单服务层每种类型的实体的这需要持续的照顾,删除,搜索,等它从 UTIL类获取的EntityManagerFactory创建的EntityManager做交易,然后关闭的EntityManager。 EMF仅在应用程序关闭时关闭。如果我理解正确,通过关闭EntityManager,我所有的对象(与它有关)都变得分离。通过改变对象中的任何值,它们不会被持久化,所以我认为是这样。

在一本书我读过,我应该做的服务类@Stateless豆,并注入的EntityManager在他们@PersistenceContext。这样,我将拥有与我拥有的许多服务类相同数量的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 

(这甚至不是。问题的,我呢,只是一个不必要的坏,直到延迟抓取进场)

如何,我认为它应该工作:

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 .:我知道这个问题非常广泛,但我希望有人可以从我的头上抹掉问题标记。

+1

这是相当宽泛的,我不确定你会得到明确的答案。我个人使用Spring的'@ Transactional'方面为我声明性地处理事务边界,并且不要手动打开或关闭实体管理器。 – chrylis

+0

你使用Spring还是Java EE? –

+0

@ArtemNovikov我正在使用Java EE。 – Peter

回答

1

在一本书中,我读过应该让服务类@Stateless bean,并将EntityManager作为@PersistenceContext注入其中。这样,我将拥有与我拥有的许多服务类相同数量的EntityManagers

不会。您将获得绑定到当前事务的持久性上下文。简而言之,容器将完成你自己正在做的事情,除了如果服务A调用服务B,服务B调用服务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()

+0

你是对的,我错过了/混合了DAO和服务层。然后,每个服务层调用都应该创建一个EM,启动一个事务,通过将EM传递给它们来调用DAO-s,然后完成事务并关闭EM? – Peter

+0

不可以。服务应该是无状态的bean,不应该处理EM。作为无状态bean,当从外部调用事务时,事务将自动启动。然后它会调用DAO,传递函数参数(如果有的话)。 DAO是一个无状态bean,或者是一个简单的CDI bean,EM被注入。 EM被绑定到调用服务时开始的事务。因此,如果服务调用三个DAO,每个都注入EM,它们将共享相同的持久化上下文和相同的事务。 –

+0

现在很清楚,非常感谢你! – Peter

1

我不知道这是你期待一个完整的答案,但只是一些使用技巧:

创建EMF是一个很难操作,同时创造EM是一个轻量级的一个。因此,EMF被推荐为单身人士,它是线程安全的。

EM不是线程安全的。你是对的,EM建议在完成交易后关闭,以释放你不再需要的实体和资源,但交易并不仅限于像在你的例子中通过id获取对象。你可以自由地获得一个对象,并做你想要的东西(例如:在你的例子中设置科洛尔),然后关闭交易 - 只需使用特殊的updateCarColor服务方法。

因为您处于Java EE环境中,所以您宁愿将EM管理留在容器中,当您在同一事务中时,它将共享相同的EM,当使用@PersistenceContext注入时。它由代理完成 - 不是真正的EM被注入EntityManager em字段,而是代理。该代理将您的呼叫传递给共享的EM。如果没有活动交易,它将创建一个新的共享EM,然后将呼叫传递给它。

+0

谢谢你。 :) – Peter