Dúvida JSF + Hibernate = 7 selects [RESOLVIDO]

6 respostas
Lennon_Manchester

Estou com uma dúvida, tenho uma aplicação em JSF com Hibernate, bem simples, um CRUD, e qndo eu chamo meu o método do meu ManagedBean de listar, essa instrução é feita 7 vezes qndo a página é redenrizada, ou seja, são feitos 7 selects no banco de dados, gostaria de saber qual é o motivo disso, se isso eh realmente uma falha do JSF.

Segue o pedaço do código:

Meu ManagedBean

public class FornecedorHandler {

	private Fornecedor fornecedor = new Fornecedor();

	public Fornecedor getFornecedor() {
		return fornecedor;
	}

	public List<Fornecedor> getFornecedores() {
		System.out.println("#Listando Fornecedores:"); // Essa instrução é executada 7 vezes qndo o jsf é renderizado
		GenericDao<Fornecedor> dao = new GenericDao<Fornecedor>(SessionFactories.currentSession(), Fornecedor.class);
		return dao.list();
	}

	public String salva() {
		System.out.println("Adicionando: " + fornecedor.getNome());
		GenericDao<Fornecedor> dao = new GenericDao<Fornecedor>(
				SessionFactories.currentSession(), Fornecedor.class);
		dao.merge(fornecedor);
		this.fornecedor = new Fornecedor();
		return "sucesso";
	}
        // outros metodos....
}

// Jsp para listar

<h:dataTable  value="#{fornecedorHandler.fornecedores}" var="f" rendered="#{not empty fornecedorHandler.fornecedores}" >
	<h:column>
		<f:facet name="header"><h:outputText value="ID"/></f:facet>
		<h:outputText value="#{f.id}"/>
	</h:column>
	<h:column>
		<f:facet name="header"><h:outputText value="Nome"/></f:facet>
		<h:outputText value="#{f.nome}"/>
	</h:column>
</h:dataTable>

Obrigado,

abs
Lennon

6 Respostas

L

Poste sua classe GenericDao também.

Lennon_Manchester

Esta ai, apesar de ser bem padrão.

public class GenericDao<T> {

	private static Logger log = Logger.getLogger(GenericDao.class);
	private Class<T> clazz;
	private Session session;

	public GenericDao(Session session, Class<T> clazz) {
		this.session = session;
		this.clazz = clazz;
	}

	@SuppressWarnings("unchecked")
	public T load(Serializable id) {
		log.info("lendo " + clazz + " com id " + id);
		return (T) session.load(clazz, id);
	}

	public void save(T t) {
		log.info("salvando " + t);
		session.save(t);
	}

	public void merge(T t) {
		log.info("salvando " + t);
		session.merge(t);
	}
	
	public void delete(T t) {
		log.info("removendo " + t);
		session.delete(t);
	}

	@SuppressWarnings("unchecked")
	public List<T> list() {
		Criteria busca = this.session.createCriteria(this.clazz);
		return busca.list();
	}
	
}
Lennon_Manchester

Pelo oq eu entendi o que acontece, isso ocorre pq o atributo é chamado diversas vezes no ciclo de vida o managed bean para montar o datatable, então ele acaba fazendo vários selects desnecessários na base, achei 2 jeitos de resolver,:

  1. Utilizando Query Cache
  2. Setar uma variavel na página e depois percorrer ela:

< c:set var="lista" value="#{bean.todosRegistros}"/> <c:forEach items="${lista}" var="reg"> </c:forEach>

Gostaria de saber oq vcs utilizam, se existe outra solução, e se o Seam resolve isso.

Obrigado,

Lennon

ps. achei a segunda solução no link: http://www.rponte.com.br/2008/02/24/aproveitando-os-beans-do-spring-em-suas-paginas-jsf/

rponte

Olá,

A EL (Expression Language) do JSF trabalha em cima do método assessores (getters e setters), e estes métodos podem ser chamados diversas vezes durante o ciclo do vida do framework, principalmente na última fase (Render Response Phase).

E por coincidência, um dos componentes que mais faz chamadas ao método getter é o h:dataTable (ou qualquer componente de iteração, como o ui:repeat do Facelets). Alguns autores, livros e tutoriais aconselham a não colocar regras/lógica custosa dentro dos métodos getters dos managed beans, principalmente se estas regras acessam recursos como banco de dados.

Caso seja necessário, por algum motivo ter a lógica de acesso ao banco no método getter então você pode evitar as inúmeras chamadas ao banco com um “cache” básico da lista, algo como:

public List&lt;Produtos&gt; getTodosOsProdutosDoBanco() { if (this.produtos == null) { this.produtos = RepositorioDeProdutos.all(); } return this.produtos; }
Assim, durante uma mesma requisição o acesso ao banco somente ocorrerá na primeira vez.
O JBoss Seam possui soluções para isso, se não me engano é uma anotação, que não me recordo no momento o nome.

Abraços e boa sorte.

Lennon_Manchester

Vlw Rafael Ponte.

Vou começar a estudar um pouco o Seam agora.

abs
Lennon

gugaa_df
Desculpem reabrir a Thread é que estou tendo um problema bastante similar, porém neste caso é diferente, eu não posso fazer o seguinte:

if  (this.produtos == null) {

this.produtos = RepositorioDeProdutos.all();

}

return this.produtos;

Porque é uma paginação sob demanda integrado o DataScroller, então a cada clique a lista obrigatoriamente tem que ser lida novamente, o problema é que tem que ser chamado no banco uma única vez e não várias vezes. Como proceder neste caso?

Estou utilizando a soluçãodo http://marcusmazzo.wordpress.com/2008/12/28/paginacao-por-demanda-com-jsf-parte1/ para a paginação sob demanda.

Alguém tem alguma idéia?

Criado 9 de junho de 2009
Ultima resposta 16 de fev. de 2010
Respostas 6
Participantes 4