我们在Web应用程序中使用EclipseLink实现JPA。我们在一个Stateless bean中使用一个具有事务作用域(JTA事务类型)的EntityManager。
根据我从几个来源找到的信息以及与我的大学讨论后的结果,这是推荐的使用entityManager的方法。但是从我个人在开发过程中的实验中,我发现了几个问题。例如,如果你调用entityManager.find(class,id),在一个方法中两次,它将返回两个不同的对象,这是预期会发生的,因为实体管理器是事务作用域,我们有两个独立的事务。所以这种方式使用更多的内存。
我的问题是,为什么我们不应该在一个有状态的bean中使用一个扩展范围的实体管理器呢?这有什么好处吗?这会导致什么问题呢?
我们用途:
@Stateless
@LocalBean
public class SessionBean{
@PersistanceContext(unitName = "name", type = PersistenceContextType.TRANSACTION)
EntityManager entityManager;
}
我想更改为:
@Stateful
@LocalBean
public class SessionBean{
@PersistanceContext(unitName = "name", type = PersistenceContextType.EXTENDED)
EntityManager entityManager;
}
1条答案
按热度按时间wwtsj6pe1#
相反!一般来说,事务作用域的实体管理器往往比扩展实体管理器更有内存效率。
首先,让我们为讨论中的主要概念命名:持久性上下文。
根据质量标准:“* 持久性上下文是一组实体示例,其中任何持久性实体标识都有一个唯一的实体示例 *"。
理解持久性上下文与JTA事务交互的方式是JPA程序员的一项基本知识。在这两个提议的场景中,我们讨论的都是容器管理的持久性上下文。那么它们是如何工作的呢?
事务范围的持久性上下文
顾名思义,一个 * 事务作用域持久化上下文 * 绑定到一个事务及其生命周期。它在事务中创建,并将在事务结束时关闭。而且,在我们的上下文中最重要的是,它是为事务创建的唯一持久化上下文。这意味着即使在事务中创建了多个
EntityManager
示例,它们将共享相同的持久上下文。这意味着注入
EntityManager
接口的不同服务bean共享同一组实体(相同的持久化上下文),尽管分配给它们的是不同的EntityManager
示例。因此,调用entityManager.find(class, id)
两次、三次或更多次,会导致返回相同的示例。即使find()
方法调用是从不同的EntityManager
示例进行的,也是如此。只要应用程序运行相同的事务,就返回相同的实体示例--不管它是从哪个EntityManager
检索的。最后,如前所述,绑定到事务意味着持久性上下文在事务关闭时也关闭。这意味着持久性上下文首先被刷新,然后在事务结束时被清除。与 * 扩展持久性上下文 * 相比,这种行为在内存分配方面表现出巨大的收益。这是因为一旦持久性上下文被清除,在事务期间检索的实体示例被垃圾收集。
让我们看一个示例中的共享持久性上下文。
扩展持久性上下文
再次引用规格:
不加选择地使用 * 扩展持久性上下文 * 的一个主要缺点是持久性上下文将在有状态会话bean的整个生命周期中存在。换句话说,在持久性上下文中存储的实体示例将不会被删除,直到bean被删除。
另一个缺点是在使用过程中要观察到复杂的事务流。想象一下从无状态会话bean调用有状态会话bean的情况。再想象一下无状态bean,在调用有状态bean之前,使用了一个 * 事务作用域实体管理器 *。这将为无状态bean中的实体管理器创建一个 * 事务作用域持久性上下文 *。现在假设有状态bean使用了一个 *扩展实体管理器 *。当无状态会话bean调用有状态会话bean时,会抛出异常。这是因为当容器尝试将 * 扩展持久性上下文 * 分配给当前事务时,已经有持久性上下文分配给该事务。
回到你的问题上,如果我理解正确的话,你想使用 * 扩展持久性上下文 * 的主要原因是它们能够将检索到的实体示例保存在内存中。(许多未使用的实体被保存在内存中),可以争辩说至少它更快。然而,即使是这种优势也可以受到质疑,因为持久性提供者倾向于实现不同的高速缓存层。
最后,我想把书 * Java EE 8中的Pro JPA 2 * 的第6章作为这个主题的一个很好的参考。