Mostrando postagens com marcador EntityManagerFactory. Mostrar todas as postagens
Mostrando postagens com marcador EntityManagerFactory. Mostrar todas as postagens

setembro 25, 2010

Trabalhando com JPA, fora de um container Java EE.

Resolvi escrever este post, para compartilhar a minha experiência com a utilização da API de Persistência do JAVA (JPA), fora de um container Java EE (ex.: TOMCAT). No meu caso, o ambiente em questão é composto por: Tomcat, Hibernate e PostgreSQL.

Ao utilizar a JPA em um ambiente "não Java EE", o desenvolvedor não pode contar com o recurso de "Dependency Injection" (pelo menos não, na camada de modelo) e deve, por conta própria, gerenciar o ciclo de vida dos Entity Managers no domain model. Não realizar esta tarefa de forma eficiente, pode resultar em situações desagradáveis, como o travamento do banco de dados por conexões abertas em excesso.

A interface EntityManager, faz a "ponte" entre o mundo orientado a objetos e o mundo relacional,  ela oferece serviços de persistência para objetos, como não é possível contar com a Dependency Injection,  é necessário utilizar a interface EntityManagerFactory, que também faz parte da API, para obter um EntityManager

Entity Managers obtidos através de uma Entity Manager Factory, são conhecidos como Applicaton-Managed Entity Managers, é de responsabilidade do desenvolvedor gerenciar seu ciclo de vida, os que são gerenciados pelo container, são conhecidos como Container-managed Entity Managers.

Como EntityManagerFactory é um objeto de carga pesada, uma idéia básica é, criar uma única factory, através de um Singleton e permitir acesso à ela de forma global. Assim, as classes responsáveis por realizar a comunicação com o banco de dados, podem obter objetos do tipo EntityManager, através desta factory compartilhada, sempre que necessário.

Uma abordagem para alcançar este objetivo envolve: 
  1. A Criação de um Listener que verifica se a aplicação foi iniciada ou finalizada;
  2. A Criação de um singleton responsável pela factory
  3. Gerenciar ciclo de vida de Entity Managers no escopo dos métodos que fazem acesso à banco de dados.
1. O Listener

public class PersistenceAppListener implements ServletContextListener {
  
     public void contextInitialized(ServletContextEvent evt) {
     }


     public void contextDestroyed(ServletContextEvent evt) {
          PersistenceManager.getInstance().closeEntityManagerFactory();
     }
}


precisamos adicioná-lo ao web.xml:

<description>ServletContextListener</description>
<listener>
     <listener-class>my.package.PersistenceAppListener</listener-class>
</listener>

A idéia do listener é observar se a aplicação foi finalizada, para liberar os recursos.


2. O Singleton

public class PersistenceManager {
 
 public static final boolean DEBUG = true;
   
   private static final PersistenceManager singleton = 
                                             new PersistenceManager();
   
   protected EntityManagerFactory emf;
   
   public static PersistenceManager getInstance() {
     
     return singleton;
   }
   
   private PersistenceManager() {
   }
  
   public EntityManagerFactory getEntityManagerFactory() {
     
     if (emf == null)
       createEntityManagerFactory();
     return emf;
   }
   
   public void closeEntityManagerFactory() {
     
     if (emf != null) {
       emf.close();
       emf = null;
       if (DEBUG)
         System.out.println("Persistence finished at " + 
                                       new java.util.Date());
     }
   }
   
   protected void createEntityManagerFactory() {
     
     this.emf = Persistence.createEntityManagerFactory("MyUnit");
     if (DEBUG)
       System.out.println("Persistence started at " + 
                                 new java.util.Date());
   }
}

O Singleton é responsável por fornecer a factory global através do método getEntityManagerFactory().

3. Utilizando Entity Managers

Segue o exemplo de código cliente, que utiliza a factory para obter um EntityManager

public class MyRepository  {
     
     // variável de instância 
     private EntityManagerFactory emf = PersistenceManager.getInstance().getEntityManagerFactory();
     
     // Insere objeto no banco
     public void add(myObject object) {
      
          EntityManager em = this.emf.createEntityManager();
          try{
               EntityTransaction et = em.getTransaction();
               try {
                    et.begin();
                    em.persist(object);
                    et.commit();
               } finally {
                    if (et.isActive()) et.rollback();
               }
          } finally {
               em.close();
          }  
     }
}

No geral, esta abordagem atendeu bem às minhas necessidades, mas eu enxerguei duas desvantagens neste modelo:
  1. A dificuldade de utilizar LAZY LOAD de objetos, pois o ciclo de vida dos Entity Managers têm escopo de método. Os relacionamentos @OneToMany e @ManyToMany, utilizam lazy load por default. Uma forma de resolver este problema é anotar forçando EAGER LOADING, ex.: @OneToMany(fetch=FetchType.EAGER)
  2. A quantidade de código extra (try's aninhados), necessário para manter a gerência de ciclo de vida dos Entity Managers, de forma eficiente.
Para saber mais: