Hibernate + Spring: sessão fechada ou transação não ativa

Estou com problemas de sessão em minha aplicação que de forma indeterminada acaba pegando uma sessão fechada. Esse problema só acontece em produção mesmo fazendo os ‘devidos’ tratamentos na aplicação.
Vejam e me respondam onde possívelmente estou errando.
Estou usando Hibernate 3.2.2 e Spring 2.5 e pool de conexões DBCP.

Meu componente que tem o sessionFactory injeta e libera as sessions p/ outros componentes via o seu singleton:

public class HibernateFactory implements Serializable {
	private static final long serialVersionUID = 5678530444200888063L;
	private SessionFactory sessionFactory;
	private static HibernateFactory instance;
	private Session session;
	public HibernateFactory() {
		sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
	}
	
	public Session getSession(){
		//session =sessionFactory.getCurrentSession();
		
		if(session == null || !session.isOpen()){
			session = sessionFactory.openSession();	
		}
		return session;
	}
	
	public static HibernateFactory getInstance(){
		if(instance == null)
			instance = new HibernateFactory();
		
		return instance;
	}
...

Se eu descomentar a linha do getCurrentSession() pego a seguinte exception:
org.hibernate.HibernateException: createCriteria is not valid without active transaction

Nos métodos que não alteram dados na base, eu não abro transação e ponho @Transaction(readonly=true), nos demais abro e fecho transações normalmente.
Abaixo segue minhas configurações que possam estar relacionadas ao problema:

web.xml

....
    <listener>
      <listener-class> org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
   
    <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:applicationContext.xml</param-value>
    </context-param>
....
	<filter>    
       <filter-name>hibernateFilter</filter-name>    
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>    
    </filter>    
	   
    <filter-mapping>    
        <filter-name>hibernateFilter</filter-name>    
        <url-pattern>/*</url-pattern>    
     </filter-mapping>

applicationContext.xml

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
     	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql://localhost:3306/BANCO?autoReconnect=true"/>
		<property name="username" value="USUARIO"/>
		<property name="password" value="SENHA"/>
		
		<property name="initialSize">
	    	<value>20</value>
	 	</property>
		<property name="maxActive">
			<value>50</value>
		</property>
		<property name="maxWait">
        	<value>20000</value>
    	</property>
		<property name="maxIdle">
			<value>10</value>
		</property>
		<property name="minIdle">
			<value>2</value>
		</property>
		<property name="removeAbandoned">  
          <value>true</value>  
	    </property>  
	    <property name="removeAbandonedTimeout">  
          <value>100</value>  
	    </property>
	     <property name="validationQuery">  
          <value>select 1</value>  
	    </property>	    
    </bean>
    
    <!-- Hibernate SessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
       <property name="dataSource"> 
          <ref local="dataSource"/> 
        </property> 
        
        <property name="configLocation">
   			<value>classpath:hibernate.cfg.xml</value>
   		</property>
		
		 
		<property  name="configurationClass">
   			<value>org.hibernate.cfg.AnnotationConfiguration</value>
		</property>
		  
    </bean> 
    
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <!-- Hibernate transaction manager-->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
       <property name="dataSource"> 
          <ref local="dataSource"/> 
       </property>
    </bean>
.....

hibernate.cfg.xml

 <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
  <property name="connection.driver_class">com.mysql.jdbc.Driver</property>

  <property name="connection.pool_size">30</property>
  <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
  <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
  
  <property name="hibernate.cache.use_structured_entries">true</property> 
  <property name="hibernate.max_fetch_depth">2</property>
  <property name="current_session_context_class">thread</property>
  
  <property name="hibernate.cache.use_second_level_cache">true</property>
  <property name="hibernate.cache.use_query_cache">true</property>

ehcache.xml

<ehcache>
	<diskStore path="java.io.tmpdir" />
	<defaultCache 
		maxElementsInMemory="200" 
		eternal="false"
		timeToIdleSeconds="120" 
		timeToLiveSeconds="120" 
		overflowToDisk="false" />
    	
     <cache  name="org.hibernate.cache.StandardQueryCache"       
		  maxElementsInMemory="500"       
		  eternal="false"       
		  timeToIdleSeconds="200"       
		  timeToLiveSeconds="500"       
		  overflowToDisk="true"/>
		     
     <cache name ="org.hibernate.cache.UpdateTimestampsCache" 
		 maxElementsInMemory ="250"
		 eternal ="true" 
		 overflowToDisk ="true"/> 
    	
	<cache name="MINHACLASSE1"
		maxElementsInMemory="30" 
		eternal="false" 
		timeToIdleSeconds="10000"
		timeToLiveSeconds="10000" 
		overflowToDisk="false" />
		
	<cache name="MINHACLASSE2"
		maxElementsInMemory="30" 
		eternal="false" 
		timeToIdleSeconds="10000"
		timeToLiveSeconds="10000" 
		overflowToDisk="false" />
</ehcache>

Também estou com o mesmo problema.
Será que alguém tem a solução?

Alguém p/ ajudar?!?!?!

Thiago, uma coisa que nao ficou muito claro: Porque voce implementou a classe HibernateFactory, se voce esta usando o filtro OpenSessionInViewFilter do Spring?

Soh para reforcar, este filtro da forma como foi configurada (url-pattern=/*) ira abrir uma transacao toda vez que for feito uma requisicao de acordo com o pattern configurado. Feito tudo que tiver que ser feito, ao realizar o Response da requisicao, a transacao eh comitada caso nenhuma Runtime exception seja lancada. Ou seja, voce ja tem um filtro que gerencia uma sessao do hibernate pra voce de forma nativa, porem, por alguma razao, vc tambem o faz com o HibernateFactory… Me pergunto se o erro nao estaria ai.

Outra coisa, voce esta trabalhando com JPA com Hibernate sendo o provider, ou apenas com o Hibernate Core?

Caso seja JPA, voce poderia trabalhar exatamente da mesma forma, porem injetando em seus DAO, o entityManager. O gerenciamento da transacao seria feito pelo Spring atraves do filtro. Acredito que tudo funcionaria corretamente dessa forma.

Infelizmente nao consigo enxergar outros erro nessas configuracoes. Da uma refletida no assunto e voltamos nos falar.

Abracos

Alex

Acho que entendi… está havendo um pouco de confusão entre na forma pego a sessão.
Amanhã cedo vou postar como tá esse lance das injeções do hibernateFactory, SessionFactory e meus componentes que usam a sessão do hibernate.

Estou usando hibernate-core.

VLW a ajuda.

Alexmdo
Olha aí meu aplicationContext.xml completo.

[code]<?xml version="1.0" encoding="UTF-8"?>

    <property name="initialSize">
        <value>20</value>
     </property>
    <property name="maxActive">
        <value>50</value>
    </property>
    <property name="maxWait">
        <value>20000</value>
    </property>
    <property name="maxIdle">
        <value>10</value>
    </property>
    <property name="minIdle">
        <value>2</value>
    </property>
    <property name="removeAbandoned">  
      <value>true</value>  
    </property>  
    <property name="removeAbandonedTimeout">  
      <value>100</value>  
    </property>
     <property name="validationQuery">  
      <value>select 1</value>  
    </property>        
</bean>

<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
   <property name="dataSource"> 
      <ref local="dataSource"/> 
    </property> 
    
    <property name="configLocation">
           <value>classpath:hibernate.cfg.xml</value>
       </property>
    
     
    <property  name="configurationClass">
           <value>org.hibernate.cfg.AnnotationConfiguration</value>
    </property>
    
</bean> 

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

<!-- Hibernate transaction manager-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
   <property name="dataSource"> 
      <ref local="dataSource"/> 
   </property>
   
</bean>

<bean id="hibernateFactory" class="br.oncase.datametrica.unimed.repository.HibernateFactory">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="atendimentoManager" class="br.oncase.datametrica.unimed.manager.AtendimentoManagerImpl">
    <property name="hibernateSessionFactory" ref="hibernateFactory"></property>
</bean>

<bean id="clienteManager" class="br.oncase.datametrica.unimed.manager.ClienteManagerImpl">
    <property name="hibernateSessionFactory" ref="hibernateFactory"></property>
</bean>

<bean id="operadorManager" class="br.oncase.datametrica.unimed.manager.OperadorManagerImpl">
    <property name="hibernateSessionFactory" ref="hibernateFactory"></property>
</bean>

<bean id="pendenciaManager" class="br.oncase.datametrica.unimed.manager.PendenciaManagerImpl">
    <property name="hibernateSessionFactory" ref="hibernateFactory"></property>
</bean>

<bean id="prestadorManager" class="br.oncase.datametrica.unimed.manager.PrestadorManagerImpl">
    <property name="hibernateSessionFactory" ref="hibernateFactory"></property>
</bean>

<bean id="tipoManager" class="br.oncase.datametrica.unimed.manager.TipoManagerImpl">
    <property name="hibernateSessionFactory" ref="hibernateFactory"></property>
</bean>
[/code]

O hibernateFactory

Abaixo um trecho do uso do HibernateFactory nos componentes que acessam o banco.

public class AtendimentoManagerImpl implements AtendimentoManager,
            Serializable {
    private HibernateFactory hibernateSessionFactory;
    
    @Override
    @Transactional(readOnly=false)
    public Atendimento criarAtendimento(Cliente c, Operador op) throws Exception {
        Session session = null;
        Transaction t = null;
        .....
        try{
            session = hibernateSessionFactory.getSession();
            t = session.beginTransaction();
                .....................
            session.save(???????);// AQUI TOW PEGANDO SESSÃO FECHADA.
            t.commit();
        }catch(Exception e){
            if(session!=null){
                session.close();
            }
            e.printStackTrace();
            throw e;
        }
        return ..........;
    }
    

O que não entendo é como pego a sessão fechada com o método getSession verificando… ainda pego sessão fechada.
Quanto tu falas p/ que eu não use um hibernateFactory é para que eu injete ele diretamente nos componentes que precisam de sessions do hibernate??

Exatamente.

Se você estivesse utilizando o Hibernate EntityManager, você poderia injetá-lo diretamente no seu DAO dessa forma:

@PersistentContext
private EntityManager em;

Pelo que entendi, você usa somente o Hibernate Core, neste caso, você poderia fazer uso do modulo Spring ORM (que você já faz) e injetar dentro do seu DAO a session do hibernate via Spring.

Nisso, voce poderia desprezar aquela classe que voce criou.

Vi que você já possue o HibernateSessionFactory no Spring. Verifica se você consegue injetar deste bean, o Session do Hibernate dentro do seu DAO.

Abracos

Tu queres que eu injete
id=“sessionFactory” class=“org.springframework.orm.hibernate3.LocalSessionFactoryBean”

direto nos DAOs?

Alex
Tu queres que eu injete o SessionFactory do Spring diretamente nos DAOs?

Eu tentaria nao injetar o SessionFactory, mas a Session originada da SessionFactory. Acredito que no Spring voce consiga invocar um metodo dentro de um bean declarado, para associa-lo na propriedade de um outro bean.

Nao sei de cabeca como seria feito isso, mas seria algo mais ou menos assim:

<bean id="bean1" class="br.com.proj.vo.Bean1">
</bean>

<bean id="bean2" class="br.com.proj.vo.Bean2"> 
  <lookup-method name="getBean2" bean="bean1"/>
</bean>

Algo parecido com isso.

Em ultimo caso, cria um HibernateDAO (classe pai de todos os seus DAOs), injete nele o SessionFactory, crie um metodo utilitario para recuperar a Session e ve se isso funciona.

Oi Alex,
Eu injetei a session factory como foi dito porém depois disso para cada acesso ao banco é necessário criar uma transação.
Usando o método getCurrentSession funcionou apenas quando criei a transação manualmente, quando tentei usar a anotação @Transactional não funciona.
Quando eu uso só a anotação a seguinte exceção é lançada:

org.hibernate.HibernateException: createCriteria is not valid without active transaction
	at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:338)
	at $Proxy27.createCriteria(Unknown Source)

Eita…rs

Thiago, faz o seguinte: Pega a documentação do Spring, vai até a seção onde fala das várias estratégias de configurar acesso a banco de dados, e tenta ver se de repente exista alguma solução próxima do seu problema. Sinceramente, estou sem idéias no momento.

A documentação deles é muito boa e tenho certeza que você encontrará exemplos para reproduzir um cenário parecido com o seu. O documento pode ser obtido em PDF no site do spring.

Desculpa não poder ajudar mais neste ponto.