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

时间:2013-11-23 18:31:37

标签: java hibernate java-ee jpa cdi

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

我对EntityManager bean中的CDI生命周期感到非常困惑,我需要一个很好的建议来清除我心中的一些东西。 通常我读过的是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<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();
    }
}

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

有人可以帮我理解我该怎么办?

4 个答案:

答案 0 :(得分:19)

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

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

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

答案 1 :(得分:12)

您可以使用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 (e.g. 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服务器,那么您必须这样做     一世。提供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 (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).

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

你打电话:

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

如果成功,您将确保致电

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

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

entityManager.getTransaction().rollBack();

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

答案 2 :(得分:5)

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

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

答案 3 :(得分:-1)

您可以配置三种类型的EM

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

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

对于应用程序管理,通常我们在一个方法中定义一个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();
        }
...
}

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

<context:annotation-config/>

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

@PersistenceContext
private EntityManager em;

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