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