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

2022-09-01 22:54:42

我正在开发一个应用程序,并且已经开始与 和 一起使用。Web 容器是 。CDIJSFJPATomcat

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

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

所以基本上我只是用普通来完成这个。但是,我不确定这是否是标准的做事方式,更重要的是,我不知道该如何关闭豆子生活结束后。作为总结:我最终有许多未关闭的连接(s),所以内存泄漏。CDIEntityManagerEntityManager

有人可以帮助我了解我应该如何继续吗?


答案 1

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

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

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

您必须在应用程序中显式打开和关闭 EM。这很简单:创建一个实体管理器Factory的应用程序范围的实例,将其注入到所有bean中。当您需要EM时,只需创建它,使用然后立即关闭,而无需等待Bean的上下文结束。因为在此配置中,打开的 EntityManager 将保留连接,而对于长期存在的 Bean,您将耗尽连接。您可以在 ObjectDB 手册的获取 JPA 数据库连接部分找到简单而全面的说明。


答案 2

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

@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 兼容范围@ViewScoped这是仅限 CDI @FlowScoped它没有@ManagedBean的等效项。

(1)真的,如果你最有兴趣使用CDI或CDI + JSF + JPA,那么将tomcat升级到TomEE或与TomEE一起使用。Tomcat + Java EE = TomEE.TomCAT 的 Java Enterprise Edition,使用 TomEE 即可获得带有 JPA 的 Tomcat。

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

(3) 雄猫 8.Tomcat 8 与 Java EE 7 保持一致。

2) 管理实体管理器生命周期

在非企业环境(例如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和事务。在这种情况下,应考虑要使用的实体管理器的正确范围。

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()


推荐