如何在CDI环境中管理EntityManager生命周期(使用Tomcat)

我正在开发一个应用程序,我已经开始使用CDI以及JSFJPA 。 Web容器是Tomcat

我对我的CDI bean中的EntityManager生命周期非常困惑,我需要一个很好的建议来清除我脑海里的一些东西。 通常我读过的是EntityManager应该主要用在Java EE容器中,使用PersistenceContext注释注入它。 那么容器就会照顾它的生命。 但是,如果您不使用Java EE容器(作为Tomcat ),那么我需要管理我的EntityManager的生命。

现在哪个是我最好的选择,使用Tomcat, CDI, JSF and JPA ? 我现在正在做的是以下内容:

 public class EntityManagerFactoryProducer { public static final String TEST = "test"; @Produces @ApplicationScoped public EntityManagerFactory create() { return Persistence.createEntityManagerFactory(TEST); } public void destroy(@Disposes EntityManagerFactory factory) { factory.close(); } } public class EntityManagerProducer { @Inject private transient Logger logger; @Inject private EntityManagerFactory emf; @Produces public EntityManager create() { return emf.createEntityManager(); } public void destroy(@Disposes EntityManager em) { em.close(); logger.debug(String.format("%s Entity manager was closed", em)); } } @Named @ViewScoped @Interceptors(LoggingInterceptor.class) public class ProductBacking implements Serializable { @Inject private ProductDAO productDAO; 

 @ViewScoped public class ProductDAOImpl implements ProductDAO, Serializable { private static final long serialVersionUID = 4806788420578024259L; private static final int MAX_RANDOMIZED_ELEMENTS = 3000; @Inject private transient Logger logger; @Inject private EntityManager entityManager; @Override public List getSuggestedProducts() { logger.debug(String.format("%s Entity manager get products", entityManager)); return entityManager.createQuery("SELECT p FROM Product p ORDER BY random()", Product.class).setMaxResults( MAX_RANDOMIZED_ELEMENTS).getResultList(); } @Override public void saveProduct(Product product) { logger.debug(String.format("%s Entity manager save product", entityManager)); entityManager.getTransaction().begin(); entityManager.merge(product); entityManager.getTransaction().commit(); } @PreDestroy void destroy() { entityManager.close(); } } 

所以基本上我只是使用普通的CDI来实现这一目标。 但是,我不确定这是否是标准的做法,更重要的是,我不知道如何在bean寿命结束后关闭EntityManager 。 总结:我最终得到许多未闭合的连接( EntityManager ),因此内存泄漏。

有人可以帮我理解我该怎么办? 非常感谢你。

这不是关于CDI。 EntityManager的生命周期取决于其类型,可以是:

  1. 容器管理的事务,
  2. 容器管理扩展,
  3. 应用程序管理的。

前两个仅在完整的应用程序服务器中可用。 因此,如果您要坚持使用servlet容器,那么您将缩小到第3个选项。

您必须在应用程序中明确打开和关闭EM。 它很简单:创建一个应用程序范围的EntityManagerFactory实例,将其注入所有bean。 当你需要EM时只需创建它,使用然后立即关闭而不等待bean的上下文结束。 因为在这种配置中,一个开放的EntityManager将保留一个连接,并且对于长寿命的bean,你将用完连接。 您可以在ObjectDB手册的“ 获取JPA数据库连接”部分中找到简单而全面的说明。

您可以使用CDI Scopes维护CDI bean状态。 实际上EntityManagerProducer#create()缺少范围。 无论您使用Weld还是OpenWebBean配置/安装CDI的RI,您都可以将cdi bean状态定义为belwo。

 @Produces @RequestScoped public EntityManager create() { return emf.createEntityManager(); } 

你的问题是

 1. CDI, JSF and JPA2. 2. Managing EntityManager lifecycle when using JPA in a non enterprise environment (eg Tomcat) 

1. CDI,JSF和JPA2。

Tomcat容器不支持CDI开箱,甚至不支持JSF,你知道开发人员必须提供JSF jars他们自己的.JSF 2.2有新的CDI兼容scops @ViewScoped这里是CDI-only @FlowScoped没有相同的对于@ManagedBean。

(1)真的如果你最感兴趣的是使用CDI或CDI + JSF + JPA,那么将tomcat升级到TomEE或者使用TomEE。 Tomcat + Java EE = TomEE.Tomcat的Java企业版,使用TomEE,您可以使用JPA获得Tomcat。

(2)如果你无法控制升级tomcat服务器,那么你必须做i。 提供CDI和其他一些jar和配置文件以及我们自己的应用程序。 II。 在tomcat中安装CDI(Weld或OpenWebBeans这些都是主要的CDI实现)

(3)Tomcat 8. Tomcat 8与Java EE 7对齐。

2)管理EntityManager生命周期

在非企业环境(例如Tomcat)或Java SE中使用JPA时,管理EntityManager生命周期是一项自定义任务。 在这种情况下,您应该考虑使用EntityManager的正确范围,在使用资源时,确保在不再需要时关闭它们始终很重要。

 There are three main types of EntityManagers defined in JPA. Container Managed and Transaction Scoped Entity Managers Container Managed and Extended Scope Entity Managers Application Managed Entity Managers 

使用JPA有两种我们可以处理的资源:EntityManager和事务。 在这种情况下,您应该考虑使用EntityManager的正确范围。

 1. An EntityManager is not a heavyload object. There is no need to use the same EntityManger longer than needed, You can't use an EntityManager instance for the whole application lifecycle (application scope) for the EntityManager is not Thread-safe) 2. It's not safe to traverse lazy-loaded relationships once the EntityManager is closed (This situation will change as of JPA 2.0). i.)Method scope (ie instantiate/destroy one EntityManager in each business method). The method scope is not enough for every situation. There could be some scenarios where you'll need a wide scope, such as the following situations: i. When transactions spread multiple business methods. ii. Need to traverse lazy-loaded relationships outside a method (eg in a JSF page). In method scope be careful to ensure the EntityManger is always closed ii.)Request scope (on-demand creation of the EntityManager instance to use within the request service) EntityManager per HTTP request strategy with the following features: i. Creation on demand of the EntityManager. ii. Lazy closing of the EntityManager. The main benefit of this scope is derived from the delayed closing of the EntityManager (it will last as long as a HTTP request is in process). Every queried entity will be managed till the end of the request and therefore during the presentation phase (the render phase in JSF for instance). 

在您的情况下,您正在使用应用程序实体管理器和应用程序管理事务,这意味着它应该处理事务的代码。 简而言之,它意味着:

你打电话:

 entityManager.getTransaction().begin(); //to start a transaction 

如果成功,你将确保打电话

 entityManager.getTranasaction().commit(); //to commit changes to database 

如果失败,您将确保致电:

 entityManager.getTransaction().rollBack(); 

现在假设你有一个容器,它知道何时调用begin(), commit() or rollback() ,这就是容器管理的事务。

主要问题是您的实体经理生产者没有范围。 因此,它依赖于永远不会被清理。 您应该为您的实体经理提供范围。

另一件事是Apache DeltaSpike已经解决了这个问题。 为什么不使用DeltaSpike? https://deltaspike.apache.org/documentation/jpa.html

您可以配置三种类型的EM

 container-managed transactional, container-managed extended, application-managed. 

我们通常使用容器管理的事务和应用程序管理。 我会举几个例子。

对于应用程序管理,通常我们在一个方法中定义一个EM。

 public List retrieve(String key) { ... EntityManager em = null; try { em = emf.createEntityManager(); Query query = em.createQuery(queryString); //get the resultList of BookingMain result = query.getResultList(); } catch (Exception e) { DAOExceptionHandler.handler(dataSource,BookingMainDAO.class, e, queryString); }finally{ em.close(); } ... } 

对于容器管理的EM,默认值为事务范围。 你需要在弹簧中配置以下注释

  

然后在你的DAO类中添加下面的注释

 @PersistenceContext private EntityManager em; 

然后在每种方法中,您都可以使用EM自动注入。 使用事务作用域EM,persistenceContext可以在同一事务中的不同方法之间传播。