JPA, Hibernate e Spring - Session Closed!

Boa tarde a todos!

Eu estou com uma aplicação web utilizando JPA, Hibernate e Spring e estou com problemas na execução de consultas.

Ao executar uma busca numa entidade que tem um relacionamento Lazy, é lançada uma exception dizendo que a sessão está fechada e por isto o relacionamento não pode ser carregado.

Estou utilizando filtro org.springframework.orm.hibernate3.support.OpenSessionInViewFilter, que teoricamente manteria a sessão aberta.

Erro:

ERROR [org.hibernate.LazyInitializationException] failed to lazily initialize a collection of role: br.com.myPackage.entities.User.roles, no session or session was closed: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: br.com.myPackage.entities.User.roles, no session or session was closed.

org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
	org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
	org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
	org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
	org.hibernate.collection.PersistentBag.toString(PersistentBag.java:506)
	java.lang.String.valueOf(String.java:2826)
	java.lang.StringBuilder.append(StringBuilder.java:115)
	br.com.egc.newOn.entities.Avaliado.toString(Avaliado.java:149)
	java.lang.String.valueOf(String.java:2826)
	java.lang.StringBuilder.append(StringBuilder.java:115)
	java.util.AbstractCollection.toString(AbstractCollection.java:422)
	com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getFormattedValue(HtmlBasicRenderer.java:511)
	com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getFormattedValue(HtmlBasicRenderer.java:534)
	com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:353)
	com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:160)
	javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:879)
	com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:308)
	com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:101)
	javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:849)
	com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:300)
	com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:101)
	javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:849)
	javax.faces.component.UIComponent.encodeAll(UIComponent.java:1663)
	javax.faces.component.UIComponent.encodeAll(UIComponent.java:1666)
	javax.faces.component.UIComponent.encodeAll(UIComponent.java:1666)
	com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:389)
	com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:127)
	com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:117)
	com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
	com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:135)
	javax.faces.webapp.FacesServlet.service(FacesServlet.java:335)
	org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
      http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">

	<persistence-unit name="my_unit_name" transaction-type="JTA">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:postgresDS</jta-data-source>
		<exclude-unlisted-classes>false</exclude-unlisted-classes>
		<properties>
			<property name="hibernate.archive.autodetection" value="class"/>
			<property name="hibernate.connection.username" value="xxxx" />
			<property name="hibernate.connection.password" value="xxxx" />
			<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
			<property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/xxxx" />
			<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider" />
			<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />

			<property name="hibernate.hbm2ddl.auto" value="create" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="true" />
			<property name="jboss.entity.manager.factory.jndi.name" value="java:/MyEntityManager" />
			<property name="hibernate.connection.release_mode" value="after_transaction" />
		</properties>
	</persistence-unit>
</persistence>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<tx:annotation-driven transaction-manager="transactionManager" />
	<context:annotation-config />
	<context:component-scan base-package="br.com.myPackage" />

	<jee:jndi-lookup id="usTx" jndi-name="java:/TransactionManager" />

	<tx:jta-transaction-manager />

	<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:/MyEntityManager" />

	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
		<property name="transactionManager" ref="usTx" />
	</bean>

	<jee:jndi-lookup id="dataSource" jndi-name="java:postgresDS" />

	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource">
			<ref local="dataSource" />
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
			</props>
		</property>
	</bean>
</beans>

DAO:


import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Component
public class MyDao<Entidade> {
    private static final Logger LOG = Logger.getLogger(MyDao.class);

    @PersistenceContext
    private EntityManager entityManager;

    public MyDao() {
    }

    @Transactional(readOnly=false, propagation=Propagation.REQUIRED)
    public void remove(Entidade entidade) {
	entityManager.remove(entidade);
    }

    @Transactional(readOnly=false, propagation=Propagation.REQUIRED)
    public void save(Entidade obj) {
	entityManager.merge(obj);
    }

    @Transactional(readOnly=false, propagation=Propagation.REQUIRED)
    public void refresh(Entidade entidade) {
	entityManager.refresh(entidade);
    }

    @Transactional(readOnly=true, propagation=Propagation.SUPPORTS)
    public List<Entidade> findAll(Class<Entidade> entityClass) {
	String qlString = "SELECT o FROM " + entityClass.getSimpleName() + " o";
	Query query = entityManager.createQuery(qlString);
	return query.getResultList();
    }

    @Transactional(readOnly=true, propagation=Propagation.SUPPORTS)
    public Entidade findByPk(Class<Entidade> entityClass, Object primaryKey) {
	return entityManager.find(entityClass, primaryKey);
    }

    public EntityManager getEntityManager() {
	return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
	this.entityManager = entityManager;
    }

}

Estou aprendendo sobre esta arquitetura e aproveito para tirar algumas dúvidas:
1 - Estou configurando a aplicação para utilizar JTA (sem EJB). Pelo que li, e só declarar na a annotation @Transaction , configurar o persistence-unit para JTA e no spring declarar o bean transactionManager=org.springframework.transaction.jta.JtaTransactionManager.
Está correto isto? Falta algo?

2 - Com JPA não precisariamos manipular a Session do hibernate. Penso que seria transparente para o desenvolvedor.

Alguém tem alguma ideia do que estou fazendo errado ou deixando de fazer algo?

Agradeço pela ajuda!

Se está usando jpa, o mais adequado seria usar OpenEntityManagerInViewFilter ao invés de OpenSessionInViewFilter.

E sobre as transações, nesse caso eu usaria JpaTransactionManager ao invés da JTA.

Blz? Flw! :thumbup:

Blz. Funcionou!

Eu fiz uma revisão das minhas configurações e adicionei OpenEntityManagerInViewFilter ao invés de OpenSessionInViewFilter, como você falou.

Só continuei utilizando o JTA, mas mesmo assim funcionou na boa!

No final, minhas configurações ficaram assim:

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd	
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd 
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"
	default-autowire="byName" default-lazy-init="false">

	<jee:jndi-lookup id="dataSource" jndi-name="java:postgresDS" />

	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<jee:jndi-lookup id="usTx" jndi-name="java:/TransactionManager" />

	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
		<property name="transactionManager" ref="usTx" />
	</bean>

	<tx:annotation-driven transaction-manager="transactionManager" />

	<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:/myEntityManager" />

	<!-- JPA annotations bean post processor -->
	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"></bean>

	<!-- procura anotacoes no pacote -->
	<context:component-scan base-package="br.com.myPackage" />

	<!-- Exception translation bean post processor -->
	<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"></bean>

	<!-- controle transacional com AOP -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<aop:pointcut id="serviceOperation" expression="execution(* br.com.myPackage.service..*Service.*(..))" />
		<aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice" />
	</aop:config>
</beans>

web.xml

         <listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>
       <filter>
		<filter-name>OpenEntityManagerInViewFilter</filter-name>
		<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>OpenEntityManagerInViewFilter</filter-name>
		<url-pattern>*.jsf</url-pattern>
	</filter-mapping>

       <persistence-unit-ref>
		<persistence-unit-ref-name>persistence/my_unit_name</persistence-unit-ref-name>
		<persistence-unit-name>my_unit_name</persistence-unit-name>
	</persistence-unit-ref>

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
	version="2.0">

	<persistence-unit name="my_unit_name" transaction-type="JTA">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:postgresDS</jta-data-source>
		<exclude-unlisted-classes>false</exclude-unlisted-classes>
		<properties>
			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="false" />
			<property name="hibernate.use_sql_comments" value="true" />
			
			<property name="jboss.entity.manager.factory.jndi.name" value="java:/myEntityManager" />
		</properties>

	</persistence-unit>
</persistence>

Nos DAOs e Services, optei por não utilizar a anotação @Transaction, deixando este controle para o Springframework (vide aop: em applicationContext.xml).

Obrigado pela ajuda “von.juliano”!

Repare que usar @Transaction também é uma forma de usar o Spring para controlar as transações, só é uma forma diferente de configurar. E sobre usar ou não a JTA, foi apenas uma sugestão, tudo depende do ambiente, o importante é configurar corretamente, como você fez aí!

Blz? Flw! :thumbup:

Sim, vc tem razão.

Nos dois casos foi uma questão de opção.

No @Transaction, minha visão foi a centralização das configurações.

Sobre o JTA, eu estou numa aplicação JEE usando o JBoss.

Vlw, pela ajuda!

QQr coisa estamos ae!