Como gerenciar o ciclo de vida do EntityManager no ambiente CDI (usando o Tomcat)

Estou desenvolvendo um aplicativo e comecei a usar o CDI juntamente com JSF e JPA . O contêiner da web é o Tomcat .

Estou muito confuso sobre o ciclo de vida do EntityManager em meus beans CDI e precisaria de um bom conselho para esclarecer algumas coisas em minha mente. Geralmente, o que eu li é que o EntityManager deve ser usado principalmente em um contêiner Java EE , injetando-o usando a anotação PersistenceContext . Então o contêiner cuida da sua vida. No entanto, se você não usar o contêiner Java EE (como Tomcat ), será necessário gerenciar a vida útil do meu EntityManager .

Quais são as minhas melhores opções agora, usando Tomcat, CDI, JSF and JPA ? O que estou fazendo atualmente é o seguinte:

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

Então basicamente eu estou usando apenas o CDI simples para conseguir isso. No entanto, não tenho certeza se essa é a maneira padrão de fazer isso e, o que é mais importante, não sei como fechar o EntityManager após o fim da vida útil do bean. Como resumo: acabo com muitas conexões não fechadas ( EntityManager ), então memory leaks.

Alguém pode me ajudar a entender como devo proceder? Muito obrigado.

Não é sobre o CDI. O ciclo de vida do EntityManager depende do seu tipo, que pode ser:

  1. transacional gerenciada por contêiner,
  2. gerenciado por contêiner estendido,
  3. gerenciado por aplicativo.

Os dois primeiros só estão disponíveis em um servidor de aplicativos completo. Então, se você vai ficar com um contêiner de servlet, você está limitado à terceira opção.

Você terá que abrir e fechar explicitamente EMs em seu aplicativo. É simples: crie uma instância de EntityManagerFactory para todo o aplicativo, insira-a em todos os seus beans. Quando você precisar de um EM apenas criá-lo, use e feche imediatamente sem esperar que o contexto do seu bean termine. Porque nesta configuração um EntityManager aberto reterá uma conexão e com beans de longa duração você ficará sem conexões. Você pode encontrar uma explicação fácil e abrangente na seção Obtenção de uma conexão de database JPA do manual ObjectDB.

Você pode manter o estado do bean CDI usando os Escopos CDI . Na verdade EntityManagerProducer#create() falta o escopo. O que quer que o RI do CDI você tenha configurado / install no tomact seja o Weld ou o OpenWebBean, você pode definir o estado do seu cdi bean como belwo.

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

Seu problema é

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

1. CDI, JSF e JPA2.

O contêiner do Tomcat não suporta o CDI fora da checkbox, mesmo que não o JSF, você sabe que os desenvolvedores tinham que fornecer o JSF para eles mesmos. O JSF 2.2 tem novos scops compatíveis com o CDI @ViewScoped aqui está o @FlowScoped que não tem um equivalente a CDI para @ManagedBean.

(1) Really Se você está mais interessado em usar CDI ou CDI + JSF + JPA, então atualize o tomcat para o TomEE ou use o TomEE. Tomcat + Java EE = TomEE. O Java Enterprise Edition do Tomcat, com TomEE você obtém o Tomcat com JPA.

(2) Se você não tem controle sobre a atualização do servidor tomcat, nesse caso você tinha que fazer i. Forneça o CDI e alguns outros arquivos jar e de configuração junto com o weapp it self. ii. Instalando o CDI no tomcat (Weld ou OpenWebBeans, estas são implementações principais do CDI)

(3) Tomcat 8. O Tomcat 8 está alinhado com o Java EE 7.

2) Gerenciando o ciclo de vida do EntityManager

O gerenciamento do ciclo de vida do EntityManager ao usar o JPA em um ambiente não empresarial (por exemplo, o Tomcat) ou o Java SE é uma tarefa personalizada. Nessa situação, você deve considerar o escopo correto do EntityManager para usar e, ao trabalhar com resources, é sempre importante garantir que eles sejam fechados quando não forem mais necessários.

 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 

Trabalhando com o JPA, existem dois tipos de resources que podemos cuidar: EntityManager e transactions. Nesse caso, você deve considerar o escopo correto do EntityManager para usar.

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

No seu caso, você está usando um gerenciador de entidades de aplicativos e uma transação gerenciada por aplicativos, o que significa que é seu código que deve manipular a transação. Em suma, isso significa:

Você chama:

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

então se o sucesso você vai garantir a chamada

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

ou em caso de falha, você não se esqueça de ligar para:

 entityManager.getTransaction().rollBack(); 

Agora imagine que você tenha um contêiner, que sabe quando chamar begin(), commit() or rollback() , essa transação gerenciada por contêiner.

O principal problema é que o produtor do gerenciador de entidades não possui escopo. Como resultado, depende que nunca seja limpo. Você deve fornecer um escopo para o seu gerente de entidade.

A outra coisa é que o Apache DeltaSpike já resolveu isso. Por que não usar o DeltaSpike? https://deltaspike.apache.org/documentation/jpa.html

Você pode configurar três tipos de EM

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

geralmente usamos transacional gerenciado por contêiner e gerenciado por aplicativo. Eu vou te dar exemplos.

Para gerenciamento de aplicativos, geralmente definimos um EM em um método.

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

para EM gerenciado por contêiner, o padrão é o escopo da transação. você precisa configurar na primavera com anotação abaixo

  

em seguida, na sua class DAO, adicione abaixo da anotação

 @PersistenceContext private EntityManager em; 

então, em cada método, você pode usar o EM auto injetado. com o EM com escopo da transação, o persistenceContext pode se propagar entre diferentes methods dentro da mesma transação.