Spring 2, JPA e Hibernate – Parte III

Spring + JPA

Após esse overview de JPA, iremos aprender a como configurar uma EntityManager com Spring 2, configurando o Hibernate como provider, em uma aplicação WEB.

Nesse exemplo, iremos utilizar a factory org.springframework.orm.jpa.LocalEntityManagerFactoryBean. Essa classe é responsável por criar um EntityManager para ser executado em ambientes Java SE. Porém, caso queira executar em ambientes J2EE utilize a factory org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.

Agora precisamos mapear as classes que contém referência ao EntityManager para realizar as operações de persistência: persist, merge, remove, find, etc. Geralmente, essas classes de acesso aos dados são os DAOs (Data Access Object).

 

Porém, para que não tenhamos que fazer injeção de dependência do EntityManager em todos os nossos DAOs, utilizaremos a “milagrosa” classe org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor. Ela procura todas as classes que contém a annotation @PersistenceContext ou @PersistenceUnit e faz a injeção de dependência “automagicamente” para você!! Portanto basta que nossos DAOs contenham a seguinte declaração:

 

public class EntityDAO{

...
  @PersistenceContext
  private EntityManager entityManager
;
...

}

Dessa forma não é necessário que seus DAOs não tenham que estender a classe org.springframework.orm.jpa.support .JpaDaoSupport para ter acesso as operações da EntityManager por meio do org.springframework.orm.jpa. JpaTemplate (método getJpaTemplate da classe JpaDaoSupport).

No seu application-context.xml, os seguintes beans, abaixo, devem ser declarados. Veja que o bean entityDAO não contém referência ao bean entityManagerFactory.

 

<bean id="entityManagerFactory"

        class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

        <property name="persistenceUnitName"

               value="persistenceUnit" />

</bean>

 

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPost

Processor" />

 

<bean id="entityDAO"

               class="example.dao.EntityDAO" />

 

Agora vamos criar nosso arquivo de configuração do JPA, o persistence.xml, para um banco de dados PostGreSQL e provider Hibernate. Lembrando que esse arquivo deve estar localizado no diretório META-INF do seu jar.

<persistence>

        <persistence-unit name="persistenceUnit"

               transaction-type="RESOURCE_LOCAL">

               <provider>org.hibernate.ejb.HibernatePersistence</provider>

               <non-jta-data-source>java:/example-ds</non-jta-data-source>

               <class>example.pojo.Entity1</class>

<class>example.pojo.Entity2</class>

               <properties>

                       <property name="hibernate.dialect"

                               value="org.hibernate.dialect.PostgreSQLDialect" />

                       <property name="transaction.factory_class"

                               value="

                               org.hibernate.transaction.JDBCTransactionFactory" />

               </properties>

        </persistence-unit>

</persistence>

 

Observe que para configurarmos o Hibernate como provider, basta mapear a classe org.hibernate.ejb.HibernatePersistence na tag provider, e definir as properties específicas do Hibernate na tag properties, como por exemplo, hibernate.dialect para o PostGreSQL.

Agora queremos delegar o controle de transações da JPA para o Spring. Considero a maneira mais fácil de fazer isso por meio da utilização de aspectos (AOP) do Spring.

Novamente no seu application-context.xml adicione a configuração do bean abaixo:

<bean id="transactionManager"

        class="org.springframework.orm.jpa.JpaTransactionManager">

        <property name="entityManagerFactory"

               ref="entityManagerFactory" />

</bean>

 

O JpaTransactionManager é recomendado para aplicações que utilizam apenas uma EntityManager. Dessa forma, não é necessário utilizar JTA, pois não estaremos acessando diversos recursos transacionais dentro de uma mesma transação.

Configuremos, agora, as classes que realizarão o acesso aos DAOs e, portanto é aonde devemos demarcar as transações. O atributo propagation configurado como REQUIRED, significa que se já não existir uma transação quando o método mapeado for invocado, o Spring inicia uma nova. Caso já exista o método é inserido no contexto da transação.

<aop:config>

        <aop:pointcut id="entityTransaction"

               expression="execution(* example.EntityControl.*(..))" />

        <aop:advisor advice-ref="entityTransactionAdvice"

               pointcut-ref="entityTransaction" />

</aop:config>

 

<tx:advice id="entityTransactionAdvice"

        transaction-manager="transactionManager">

        <tx:attributes>

               <tx:method name="persistEntity" propagation="REQUIRED" />

               <tx:method name="removeEntity" propagation="REQUIRED" />

               <tx:method name="mergeEntity" propagation="REQUIRED" />

               <tx:method name="findEntity" propagation="REQUIRED" />

        </tx:attributes>

</tx:advice>

 

Poderíamos ter configurado as transações para que o JpaTransactionManager fosse injetado no código do EntityControl, automaticamente, por meio da utilização da annotation @Transactional, nos métodos declarados acima.

<tx:annotation-driven transaction-manager="jpaTransactionManager" proxy-target-class="false"/>

Porém, isso não seria considerado uma boa prática, pois essa annotation faz parte da API do Spring 2 (org.springframework.transaction.annotation.Transactional ), e dessa forma seu código estaria dependente do framework Spring.

 

Hibernate Open Session In View

O Spring fornece suporte ao famoso pattern Open Session in View, para permitir que sejam carregados relacionamentos do tipo LAZY, mesmo após a transação ter finalizado.

 

O problema é quando as Entities carregadas do banco de dados são passadas para a camada de apresentação em seu estado detached os atributos anotados com fetch=LAZY podem estar indisponíveis. O filter serve justamente para que não tenha que ser criada uma nova Hibernate Session para carregar esses dados.

 

Isso é feito através do filter org.springframework.orm.hibernate.support .OpenSessionInViewFilter. Ele faz com que existe apenas uma Hibernate Session para cada thread (no caso das aplicações WEB, um Hibernate Session para cada request).

 

Para habilitar o filter, basta configurá-lo no seu arquivo web.xml, conforme abaixo. A tag url-pattern deve conter o padrão de endereços da sua aplicação. Neste exemplo, as URLs que terminam com .do, por exemplo, http://localhost:8080/testeEntity.do.

 

<filter>

        <filter-name>openEntityManager</filter-name>

        <filter-class>

               org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter

        </filter-class>

</filter>

<filter-mapping>

        <filter-name>openEntityManager</filter-name>

        <url-pattern>*.do</url-pattern>
</
filter-mapping>

Conclusão

Vimos nesse artigo os principais aspectos da nova API de persistência da especificação EJB 3, a JPA.

Foram discutidas, também, boas práticas de implementação dessa nova API, e como integrá-la com Spring 2, utilizando o Hibernate como provider, por meio apenas da configuração do persistence.xml, possibilitando a utilização de qualquer meio de persistência, por meio de uma implementação padrão.

Leia todos os artigos da série