Hoje li um post antigo, mas que achei interessante sobre padrão de projeto DAO (http://www.rponte.com.br/tag/padraodeprojeto/).
Gostaria de saber como o pessoal está desenvolvendo aplicações JSF 2.0 com Hibernate, se estão ou não utilizando DAO ou se utilizam os próprios Beans (ou CDI) já fazendo direto a persistência dos dados?
Estou criando uma aplicação e realmente estou confuso, pois estou vendo uma camada a mais com pouco sentido, sendo que posso facilmente utilizar o Bean para fazer direto as persistências e regras de negócio.
Ola eu costumo fazer assim, e é claro voce pode melhorar criando DAO generico, as vantagens é que a visão não sabe quem faz a persistencia ou persistido não sabe que o persistiu ou seja voce pode mudar a camada de visualização: beans e xhtml, html, jsp, ou até mesmo mudar a camada de persistencia sem mexer nas outras classes.
@Entity
public class Usuario implements Serializable {
@Id
@GeneratedValue
private Integer codigo;
private String nome;
private String email;
public interface UsuarioDAO {
public void salvar(Usuario usuario);
public void atualizar(Usuario usuario);
public void excluir(Usuario usuario);
public Usuario carregar(Integer codigo);
public Usuario buscaPorLogin(String login);
public List<Usuario> lista();
}
public class UsuarioDAOHibernate implements UsuarioDAO {
private Session session;
public void setSession(Session session) {
this.session = session;
}
public void salvar(Usuario usuario) {
this.session.save(usuario);
}
public class UsuarioRN {
private UsuarioDAO usuarioDAO;
public UsuarioRN() {
this.usuarioDAO = DAOFactory.criarUsuarioDAO();
}
public class DAOFactory {
public static UsuarioDAO criarUsuarioDAO() {
UsuarioDAOHibernate usuarioDao = new UsuarioDAOHibernate();
usuarioDao.setSession(HibernateUtil.getSessionFactory().getCurrentSession());
return usuarioDao;
}
É isso ai mas se aparecer ideia melhor é bem vindo para mim também
Obrigado valdirmf pela resposta. Só para informação, eu já tenho um projeto com Hibernate todo com o Dao criado.
Só fiquei na dúvida pelo que li no Blog que referenciei anteriormente, me deixou pensando.
Tenho outra dúvida ainda referente ao JSF + Hibernate, que é onde iniciar e fechar a Session.
Como iniciei meu projeto agora, fiz um teste para ver se estaria tudo Ok com meu projeto do Hibernate adicionado como dependente do projeto JSF e se o trabalho iria ser feito corretamente ao mandar salvar.
Segui conforme código abaixo na minha classe ClienteBean:
public String listaClientes() {
this.clientes = null;
this.endereco = null;
Session s = new HibernateUtil().getSession();
ClienteDao dao = new ClienteDao(s);
this.clientes = dao.listarTodos();
s.close();
return "listaClientes";
}
public String novoCliente() {
this.cliente = new Cliente();
this.endereco = new Endereco();
return "novoCliente";
}
public String salvaCliente() {
Session s = new HibernateUtil().getSession();
this.cliente.setEndereco(this.endereco);
ClienteDao dao = new ClienteDao(s);
dao.salvar(this.cliente);
s.close();
return this.listaClientes();
}
Tentando explicar o que eu fiz, ao clicar no botão listar clientes o sistema irá trazer todos os clientes conforme método listaClientes().
Ao clicar em novo cliente estou criando um novo objeto Cliente e indo para página de cadastro.
Na página de cadastro, ao mandar salvar o cliente estou utilizando as classes Dao para este trabalho.
Funcionou, mas acredito não ser a melhor forma de fazer.
O motivo de achar isto é porque em todas as classes onde estou ou chamando dados do banco, ou salvando, eu gero uma nova sessão (Session).
A pergunta é:
Onde seria o melhor lugar para eu abrir e fechar minha sessão?
Existe um padrão chamando OpenSessionInView, que até é implementado por frameworks como o Spring, através de filtros.
Nele, você abre uma sessão a partir do momento que precisa acessar o banco e só a fecha após a view ter sido completamente renderizada. Ou seja, em um formulário de uma página web, ao clicar em um botão “Pesquisar” a sessão seria aberta, a pesquisa feita, os dados processados pelo seu bean e renderizados na view. Após tudo isso, a sessão é fechada. No caso de web, acho que esse padrão também pode ser chamado de OneSessionPerRequest (uma sessão por requisição).
O Spring faz isso, como falei, mas você também pode implementar seu próprio filtro pra fazer esse trabalho.
[quote=valdirmf]Olha eu ainda sou um mero aprediz logo e estou pedalando também, mas segue a forma como eu faço para controlar as sessões
criei um filtro lembre que o filtro dever ser declarado no web.xml
[code]
public class ConexaoHibernateFilter implements Filter {
private SessionFactory sf;
public void init(FilterConfig filterConfig) throws ServletException {
this.sf = HibernateUtil.getSessionFactory();
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
try {
//inicia a transação
this.sf.getCurrentSession().beginTransaction();
//permite passar o fluxo adiante
chain.doFilter(servletRequest, servletResponse);
//commita a transação
this.sf.getCurrentSession().getTransaction().commit();
//fecha a sessão
this.sf.getCurrentSession().close();
} catch (Throwable ex) {
try {
if (this.sf.getCurrentSession().getTransaction().isActive()) {
this.sf.getCurrentSession().getTransaction().rollback();
}
} catch (Throwable t) {
t.printStackTrace();
}
throw new ServletException(ex);
}
}
[/code][/quote]
Só cuidado com esse catch(Throwable) ali. Tente sempre capturar as exceções que você sabe que podem acontecer dentro do try. Não se captura Exceptions genéricas e nem Throwables (e muito menos Errors).
Eu estava abrindo a transação direto no Dao, então pelo que entendi isto está errado.
Eu posso fazer isto direto no filtro e me preoculpar no Dao apenas em salvar, alterar, excluir, entre outros como a regra de negócio.
Então o correto é usar isto nos filtros ?
Vcs tem alguma sugestão de material para entender o Spring e usar o OpenSessionInView?
[quote=gmantovani2005][quote=valdirmf]Olha eu ainda sou um mero aprediz logo e estou pedalando também, mas segue a forma como eu faço para controlar as sessões
criei um filtro lembre que o filtro dever ser declarado no web.xml
public class ConexaoHibernateFilter implements Filter {
private SessionFactory sf;
public void init(FilterConfig filterConfig) throws ServletException {
this.sf = HibernateUtil.getSessionFactory();
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
try {
//inicia a transação
this.sf.getCurrentSession().beginTransaction();
//permite passar o fluxo adiante
chain.doFilter(servletRequest, servletResponse);
//commita a transação
this.sf.getCurrentSession().getTransaction().commit();
//fecha a sessão
this.sf.getCurrentSession().close();
} catch (Throwable ex) {
try {
if (this.sf.getCurrentSession().getTransaction().isActive()) {
this.sf.getCurrentSession().getTransaction().rollback();
}
} catch (Throwable t) {
t.printStackTrace();
}
throw new ServletException(ex);
}
}
o filter mapping, mapeia todas as requisições do tipo jsf
Eu estava abrindo a transação direto no Dao, então pelo que entendi isto está errado.
Eu posso fazer isto direto no filtro e me preoculpar no Dao apenas em salvar, alterar, excluir, entre outros como a regra de negócio.
Então o correto é usar isto nos filtros ?
Vcs tem alguma sugestão de material para entender o Spring e usar o OpenSessionInView?[/quote]
Sim, a ideia é que seus DAOs nem saibam onde as sessões são controladas. Eles simplesmente as usam (e esperam que haja um disponível).
Quanto ao OpenSessionInViewFilter do Spring, aqui no GUJ mesmo você encontra tópicos sobre:
Estou tentando fazer o exemplo do Open Session in View do Hibernate, mas quando mando listar os registros de cliente, está apresentando erro.
Estou achando que possa ser a configuração, mas antes de mudar gostaria de saber se deveria funcionar da forma que está.
HibernateUtil
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import entities.*;
public class HibernateUtil {
private static final SessionFactory factory;
static {
Configuration cfg = new Configuration();
// adicionar classes mapeadas
cfg.addAnnotatedClass(Endereco.class);
cfg.addAnnotatedClass(Cliente.class);
cfg.addAnnotatedClass(Empresa.class);
cfg.addAnnotatedClass(Profile.class);
cfg.addAnnotatedClass(User.class);
cfg.addAnnotatedClass(Projeto.class);
// factory = cfg.buildSessionFactory();
factory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return factory; //.openSession();
}
}
ClienteDao
import java.util.List;
import org.hibernate.Session;
import entities.Cliente;
public class ClienteDao implements Dao<Cliente> {
private Session session;
public ClienteDao() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
@Override
public void salvar(Cliente t) {
this.session.save(t);
}
}
O Código de Erro é :
javax.servlet.ServletException: org.hibernate.HibernateException: No CurrentSessionContext configured!
javax.faces.webapp.FacesServlet.service(FacesServlet.java:321)
No Exemplo que estou vendo no site do Hibernate, está com hibernate.cfg.xml e não com hibernate.properties como estou usando. Está vai ser o problema ou está faltando alguma coisa?
gmantovani2005, pode postar seu hibernate.properties?
Acho que faltou, como a exceção mesmo diz, você configurar um “current session context”. Aqui tem alguém que relatou o mesmo problema que você, mas ele usa um hibernate.cfg.xml. Pro properties acho que o nome da propriedade é o mesmo (current_session_context_class):
[quote=Trebloc]gmantovani2005, pode postar seu hibernate.properties?
Acho que faltou, como a exceção mesmo diz, você configurar um “current session context”. Aqui tem alguém que relatou o mesmo problema que você, mas ele usa um hibernate.cfg.xml. Pro properties acho que o nome da propriedade é o mesmo (current_session_context_class):
Adicionei o “current session context” e mesmo assim apresenta erro, já tentei de varias formas.
Veja o erro:
javax.servlet.ServletException: org.hibernate.HibernateException: No session currently bound to execution context
javax.faces.webapp.FacesServlet.service(FacesServlet.java:321)
root cause
javax.faces.el.EvaluationException: org.hibernate.HibernateException: No session currently bound to execution context
javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:98)
com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:98)
javax.faces.component.UICommand.broadcast(UICommand.java:311)
javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:781)
javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1246)
com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:77)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
root cause
org.hibernate.HibernateException: No session currently bound to execution context
org.hibernate.context.ManagedSessionContext.currentSession(ManagedSessionContext.java:74)
org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
com.gmail.gmantovani2005.database.ClienteDao.<init>(ClienteDao.java:13)
com.gmail.gmantovani2005.beans.ClienteBean.listaClientes(ClienteBean.java:26)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.apache.el.parser.AstValue.invoke(AstValue.java:262)
org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278)
com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:102)
javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:84)
com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:98)
javax.faces.component.UICommand.broadcast(UICommand.java:311)
javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:781)
javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1246)
com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:77)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
[quote=Trebloc]Como você definiu que seu contexto de sessão é gerenciado pela org.hibernate.context.ManagedSessionContext, você tem que fazer desse modo aqui:
Desculpa minha ignorância, mas eu estava me matando atoa…
Eu fiz a declaração do Filtro por anotação e estava errado…Tirei a anotação e declarei o filtro por web.xml e agora está funcionando.
Agora que entendi isto vou terminar o que eu estava fazendo e depois usar o framework do Spring (tentar).
Tenho mais uma pergunta sobre a utilização dos Filtros.
Se eu for utilizar um pequeno controle de usuário também usaria filtros.
Dai eu crio outro filtro ou utilizo o mesmo?
[quote=gmantovani2005][quote=Trebloc]Como você definiu que seu contexto de sessão é gerenciado pela org.hibernate.context.ManagedSessionContext, você tem que fazer desse modo aqui:
Desculpa minha ignorância, mas eu estava me matando atoa…
Eu fiz a declaração do Filtro por anotação e estava errado…Tirei a anotação e declarei o filtro por web.xml e agora está funcionando.
Agora que entendi isto vou terminar o que eu estava fazendo e depois usar o framework do Spring (tentar).
Tenho mais uma pergunta sobre a utilização dos Filtros.
Se eu for utilizar um pequeno controle de usuário também usaria filtros.
Dai eu crio outro filtro ou utilizo o mesmo?[/quote]
Poderia ser feito tudo em um único filtro, mas normalmente separa-se as responsabilidades: cada filtro é responsável por uma única coisa.
Só lembre-se que os filtros são uma cadeia:
Contêiner -> Filtro1 -> Filtro2 -> Filtro3 -> … -> FiltroN -> Servlet (FacesServlet, no caso do JSF)
E a ordem em que são chamados você define no web.xml (os que forem definidos primeiro, vem primeiro na cadeia).
Depois que comecei a utilizar o OpenSessionInView como discutido acima, o botão salvar e listar aparentemente funcionam corretos, mas estou com problema no botão alterar e excluir.
Não sei se isto pode estar influenciando, mas quando inicio o servidor, aparece um erro que não consegui descobrir o que é.
Segue abaixo o erro:
SEVERE: Exception loading sessions from persistent storage
java.io.InvalidObjectException: could not resolve session factory during session deserialization [uuid=2b8c53de-b2a8-45e6-9391-ab84f9a775df, name=null]
at org.hibernate.impl.SessionFactoryImpl.deserialize(SessionFactoryImpl.java:1322)
at org.hibernate.impl.SessionImpl.readObject(SessionImpl.java:2140)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at org.apache.catalina.session.StandardSession.readObject(StandardSession.java:1600)
at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:1073)
at org.apache.catalina.session.StandardManager.doLoad(StandardManager.java:284)
at org.apache.catalina.session.StandardManager.load(StandardManager.java:204)
at org.apache.catalina.session.StandardManager.startInternal(StandardManager.java:470)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5241)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1033)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:774)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1033)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:291)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:727)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.startup.Catalina.start(Catalina.java:620)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:303)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:431)
Ainda continuo com o erro acima referente a inicialização do Apache quando mando rodar a aplicação.
Isto aparentemente não está interferindo, mas o erro existe.
O que acontece agora é que tive que colocar um flush antes do commit. No exemplo passado pelo Hibernate isto acontecia apenas em uma cituação, eu coloquei nas duas.
if (request.getAttribute(END_OF_CONVERSATION_FLAG) != null ||
request.getParameter(END_OF_CONVERSATION_FLAG) != null) {
currentSession.flush();
currentSession.getTransaction().commit();
currentSession.close();
httpSession.setAttribute(HIBERNATE_SESSION_KEY, null);
} else {
currentSession.flush(); // adicionado para conseguir excluir e alterar
currentSession.getTransaction().commit();
httpSession.setAttribute(HIBERNATE_SESSION_KEY, currentSession);
}
O único problema agora é que quando mando excluir, os registros da listagem não estão atualizando.
Eu fiz da seguinte forma:
Tenho um método listar para abrir a tabela de clientes com o seguinte código:
public String listaClientes() {
this.clientes = null;
this.cliente = null;
this.endereco = null;
this.clientes = new ClienteDao().listarTodos();
return "listaClientes";
}
Dentro da listagem de clientes (JSF Page) eu tenho um link que chama o botão excluir com o seguinte código:
public String excluiCliente() {
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
Long idCliente = Long.valueOf(params.get("idCliente"));
ClienteDao dao = new ClienteDao();
this.cliente = dao.get(idCliente);
dao.excluir(this.cliente);
return this.listaClientes();
}
O registro é excluído com sucesso, mas os registros da página não são atualizados.
O que posso fazer para resolver?
Quando eu clico em novo ou em alterar isto funciona, sendo que a chamada ao listaClientes() ocorre em todos da mesma forma.