HibernateUtil, Reaproveitar sessão ou não?

Amigos,

Estou a semanas lendo bastante sobre as formas de lidar com as sessões do Hibernate em ambientes web e mesmo após ler o batido link Open Session in View e Using Hibernate with Tomcat além de dezenas de posts em diversos fóruns não consegui chegar a uma conclusão satisfatória.

Vou mostrar as formas que vi as sessões sendo tratadas nas pesquisas que fiz e convido os colegas a darem suas opiniões sobre o que acham mais aconselhado.

OBS: Levem em consideração uma aplicação de grande porte com elevado número de acessos simultâneos.

Em relação a forma mais adequada de tratar as chamadas as sessões, transações e commits, concluí (mas posso estar enganado) que é através de um filtro.

Configurando o filtro no web.xml:

	<filter>
		<filter-name>HibernateSessionFilter</filter-name>
		<filter-class>seuPacote.HibernateSessionFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>HibernateSessionFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

Implementação do HibernateSessionFilter:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.hibernate.Session;


public class HibernateSessionFilter implements Filter {

	public void destroy() {
	}
	
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
		

        Session session = HibernateUtil.getSession();


		try {
			
			session.beginTransaction();
			
			((HttpServletRequest) req).setAttribute("sessaoHibernate", session);
			fc.doFilter(req, res);
			
			session.getTransaction().commit();
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	public void init(FilterConfig arg0) throws ServletException {
	}

}

O que desejo concluir é a melhor forma de retorno do método HibernateUtil.getSession()

1 - A primeira forma é fazer o HibernateUtil retornar uma nova sessão a cada chamada do método getSession():

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {


	private static SessionFactory fabricaDeSessao;

	static {

		Configuration cfg = new AnnotationConfiguration().configure();
		fabricaDeSessao = cfg.buildSessionFactory();
	}

	public static Session getSession() {
		return fabricaDeSessao.openSession();
	}

	public static SessionFactory getSessionFactory() {
		return fabricaDeSessao;
	}

}

Neste caso devemos acrescentar ao HibernateSessionFilter após o catch as linhas abaixo para que a sessão seja finalizada:

finally {
			session.close();
		}

2 - A segunda maneira é usar o padrão ThreadLocal. Fiquei em dúvida se o Hibernate 3.2 já implementa nativamente ou não este padrão.
Não consegui compreender o funcionamento deste padrão. Se tudo é declarado como static concluo então que há apenas uma instância de ThreadLocal em toda aplicação e que em todos os momentos onde chama-se os métodos get ou set da ThreadLocal é referenciado um mesmo objeto do tipo ThreadLocal. Então porque utilizar o ThreadLocal e não o padrão Singleton simples, definindo apenas uma Session static?

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateUtil {

	private static final SessionFactory sessionFactory;
	
	private static ThreadLocal<Session> sessions = new ThreadLocal<Session>();

	static {
		sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
	}

	public static Session getSession() {
		
		if (sessions.get() != null) {
		}
		sessions.set(sessionFactory.openSession());
		return sessions.get();
	}

	public static void closeCurrentSession() {
		sessions.get().close();
		sessions.set(null);
	}

	public static Session currentSession() {
		return sessions.get();
	}
	
	public static SessionFactory getSessionFactory() {
	    return sessionFactory;
	}
	
}

3 - A terceira forma que encontrei mas não compreendi parece utilizar o mesmo padrão do item 2 porém já está implementado nativamente no Hibernate 3.2

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
	
	private static SessionFactory fabricaDeSessao;
	
	static {
		
		Configuration cfg = new AnnotationConfiguration();
		cfg.configure();
		
		fabricaDeSessao = cfg.buildSessionFactory();
	}
	
	public static Session getSession() {
		
		if (fabricaDeSessao.getCurrentSession() == null) {
			fabricaDeSessao.openSession();
		}
		
		return fabricaDeSessao.getCurrentSession();
	}
	
	
}

É necessário acrescentar ao hibernate.cfg.xml a linha:

<property name="current_session_context_class">thread</property>

Resumindo:

1 - Utilizar o Filtro é a melhor forma ou não?
2 - É melhor abrir uma Sessão para cada requisição? Ou é melhor reaproveitar a mesma sessão?
3 - Construir uma única fábrica é o mais recomendado devido ao custo mas é recomendado utilizar uma única sessão para toda aplicação?
4 - E por fim e mais importante: Qual a melhor implementação do HibernateUtil e HibernateSessionFilter?

Espero que este POST gere uma grande quantidade de respostas pois servirá de ajuda para muitos.

Grande abraço a todos,
Tássio Coêlho

Olá, sei que já faz um tempinho, mas comecei a estudar o Hibernate a pouco tempo e atualmente estou tendo a mesma dúvida que voce escreveu acima. Como já faz algum tempo, será que voce já conseguiu esclarece-las ? Se sim, qual a conclusão que voce chegou ?

Olha eu uso de várias maneira, e agora to usando Facade !os 2 funcionam !

Confesso q não li muito…

masss… abra a session, e depois feche… não reaproveite… use C3P0 pra reaproveitar

Olá Tássio,

Estou no mesmo dilema que você. Estamos implantando um sistema WEB de grande porte e estamos com problemas nas transações e ao reconetar no banco quando cai a conexão ou quando o DBA mata o serviço de banco.

Qual das 3 opções você utilizou?

A primeira de utilizar no Filter é um tanto quanto arriscada, pois ficará na sessão da aplicação e alguém pode fechar o browser sem deslogar.

A segunda é a que eu utilizo, mas precisa de melhorias.

A terceira estou tentando implementar mais nao estou conseguindo.

Voce pode me enviar seu HibernateUtil e sua configuração . cfg para eu ver como tu fez.

Att,

[quote=Lavieri]Confesso q não li muito…

masss… abra a session, e depois feche… não reaproveite… use C3P0 pra reaproveitar[/quote]

Olá Lavieri, apesar do topico ser antigo, mas pra mim é atual…rss
Como assim, abra a sessão de depois fecha? Ser sua requisição tiver 10 inserts vc abriria 10 sessões e fechariam elas?

Obrigado
Ademir

Essa é uma dúvida que tenho até hj, pq com EclipseLink, eu abro uma entityManager por View(FRame, internalFrame, Dialog… etc…) e ela fica aberta até que a View seja fechada… assim realizando o controle de commit, begin e rollback das transacoes.

AGora com hibernate…?