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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20166218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 23:39:32  来源:igfitidea点击:

How to manage EntityManager life-cycle in CDI environment (using Tomcat)

javahibernatejakarta-eejpacdi

提问by Ioan

I am developing one application and I have started to use CDIalong with JSFand JPA. The web container is Tomcat.

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

I am very confused about EntityManagerlife-cycle in my CDIbeans and I would need a good advise to clear some stuff in my mind. Generally what I've read is that EntityManagershould be used mainly in a Java EEcontainer, injecting it using PersistenceContextannotation. So then the container takes care about its life. However, if you do not use Java EEcontainer(as Tomcat), then I need to manage my EntityManager's life.

我对bean 的EntityManager生命周期感到非常困惑CDI,我需要一个好的建议来清除我脑海中的一些东西。一般来说,我读到的是EntityManager应该主要在Java EE容器中使用,使用PersistenceContext注释注入它。因此,容器会照顾它的寿命。但是,如果您不使用Java EEcontainer(as Tomcat),那么我需要管理我EntityManager的生活。

Which are my best options now, using Tomcat, CDI, JSF and JPA? What I am currently doing now is the following:

哪些是我现在最好的选择,使用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<Product> 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();
    }
}

So basically I am just using plain CDIto accomplish this. However, I am not sure if this is standard way of doing it, and what is more important, I do not know how to close the EntityManagerafter bean life is over. As as summary: I end up with many unclosed connections (EntityManagers), so memory leak.

所以基本上我只是使用普通CDI来完成这个。但是,我不确定这是否是标准的做法,更重要的是,我不知道EntityManager在 bean 生命结束后如何关闭。作为总结:我最终有很多未关闭的连接(EntityManagers),所以内存泄漏。

Can someone help me understand how should I proceed?

有人可以帮助我了解我应该如何进行吗?

采纳答案by Yuri

It's not about CDI. EntityManager's life cycle depends on its type, which can be:

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

  1. container-managed transactional,
  2. container-managed extended,
  3. application-managed.
  1. 容器管理的事务性,
  2. 容器管理的扩展,
  3. 应用程序管理。

The first two are only available in a full-blown application server. So if you're going to stick with a servlet container you're narrowed to the 3rd option.

前两个仅在成熟的应用程序服务器中可用。因此,如果您要坚持使用 servlet 容器,那么您只能选择第三个选项。

You will have to explicitly open and close EMs in your application. It's straightforward: create an application-wide instance of EntityManagerFactory, inject it to all your beans. When you need an EM just create it, use and then immediately close without waiting for your bean's context to end. Because in this configuration an open EntityManager will retain a connection and with long-lived beans you'll run out of connections. You can find an easy and comprehensive explanation in the Obtaining a JPA Database Connectionsection of the ObjectDB manual.

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

回答by John Ament

The main problem is that your entity manager producer has no scope. As a result, it's dependent which never gets cleaned up. You should provide a scope for your entity manager.

主要问题是您的实体管理器生产者没有范围。结果,它是永远不会被清理的依赖。您应该为实体管理器提供一个范围。

The other thing is that Apache DeltaSpike already has this solved. Why not use DeltaSpike? https://deltaspike.apache.org/documentation/jpa.html

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

回答by Asif Bhutto

You can maintain the CDI bean state using CDI Scopes. Actually EntityManagerProducer#create()missing the scope. What ever the RI of CDI you have configure/install in tomact either its Weld or OpenWebBean, you can define your cdi bean state as belwo.

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

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

Your problem are

你的问题是

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

1. CDI, JSF and JPA2.

1. CDI、JSF 和 JPA2。

Tomcat container not support the CDI out the box, even not the JSF, you know developers had to supply the JSF jars them self's.JSF 2.2 has new CDI compatible scops @ViewScoped here's the CDI-only @FlowScoped which doesn't have an equivalent for @ManagedBean.

Tomcat 容器不支持开箱即用的 CDI,甚至不支持 JSF,您知道开发人员必须自己提供 JSF jars。JSF 2.2 具有新的 CDI 兼容范围 @ViewScoped 这里是 CDI-only @FlowScoped,它没有等效项对于@ManagedBean。

(1) Really If you are most interested to use CDI or CDI+JSF+JPA , then upgrade tomcat to TomEE or go with TomEE. Tomcat + Java EE = TomEE.The Java Enterprise Edition of Tomcat,With TomEE you get Tomcat with JPA.

(1) 真的 如果你最有兴趣使用 CDI 或者 CDI+JSF+JPA ,那么升级 tomcat 到 TomEE 或者使用 TomEE。Tomcat + Java EE = TomEE。Tomcat的Java企业版,有了TomEE,你就可以得到带有JPA的Tomcat。

(2) If you no control over upgrading the tomcat server, in that case you had to do i. Supply CDI and some others jar and configuration files along with weapp it self. ii. Installing CDI in tomcat (Weld, or OpenWebBeans these both are major CDI implementations)

(2) 如果您无法控制升级 tomcat 服务器,在这种情况下您必须执行 i。提供 CDI 和其他一些 jar 和配置文件以及 weapp it self。ii. 在 tomcat 中安装 CDI(Weld 或 OpenWebBeans,它们都是主要的 CDI 实现)

(3) Tomcat 8. Tomcat 8 is aligned with Java EE 7.

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

2) Managing EntityManager lifecycle

2) 管理 EntityManager 生命周期

Managing EntityManager lifecycle when using JPA in a non enterprise environment (e.g. Tomcat) or Java SE is a custom task. In this situation, you should consider the right scope of the EntityManager to use and while working with resources it's always important to ensure they are closed when not longer needed.

在非企业环境(例如 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

Working with JPA there are two kind of resources we can take care of: EntityManager and transactions. In this case, you should consider the right scope of the EntityManager to use.

使用 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 (i.e. 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 (e.g. 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).

In your case your are using application entity manger and application managed transaction it means its your code which is supposed to handle the transaction. In a nutshell it means:

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

You call:

你打电话:

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

then if success you will ensure to call

那么如果成功,您将确保致电

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

or in case of failure you will make sure to call:

或者在失败的情况下,您将确保致电:

entityManager.getTransaction().rollBack();

Now imagine you have a container, which knows when to call begin(), commit() or rollback(), thats container managed transaction.

现在想象你有一个容器,它知道什么时候调用begin(), commit() or rollback(),那就是容器管理的事务。

回答by Scarlett

You can configure three types of EM

您可以配置三种类型的 EM

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

we usually use container-managed transactional and application-managed. I will give you examples.

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

For application-managed, usually we define one EM within one method.

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

public List<BookingMainDO> 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();
        }
...
}

for container managed EM,the default is transaction scoped. you need to configure in the spring with below annotation

对于容器管理的 EM,默认为事务范围。您需要在 spring 中使用以下注释进行配置

<context:annotation-config/>

then in your DAO class add below annotation

然后在您的 DAO 类中添加以下注释

@PersistenceContext
private EntityManager em;

then in each method, you can use the EM auto injected. with the transaction scoped EM, the persistenceContext can propagate between different methods within the same transaction.

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