[RESOLVIDO] Hibernate VRaptor AlteracaoDeProduto e RemocaoDeProduto

Olá caros amigos,

                     estou aprendendo a utilizar o Hibernate e estou tentando fazer o desafio 16(Refatore as outras classes de teste, AlteracaoDeProduto e RemocaoDeProduto, para utilizar o ProdutoDao) da apostila FJ28 VRaptor e me esta aparecendo um erro quando tento atualizar ou remover um produto.

Bem meu ProdutoDAO está assim:

[code]package br.com.caelum.goodbuy.dao;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import br.com.caelum.goodbuy.infra.CriadorDeSession;
import br.com.caelum.goodbuy.modelo.Produto;

public class ProdutoDao {
private final Session session;

public ProdutoDao() {
	this.session = CriadorDeSession.getSession();
}

public void salva(Produto produto) {

	Transaction tx = session.beginTransaction();
	session.save(produto);
	tx.commit();
}

public void altera(Produto produto) {
	Transaction tx = session.beginTransaction();
	session.update(produto);
	tx.commit();
}

public void remove(Produto produto) {
	Transaction tx = session.beginTransaction();
	session.delete(produto);
	tx.commit();
}

public List<Produto> listaTudo() {
	return this.session.createCriteria(Produto.class).list();
}

}
[/code]

AlteracaoDeProduto:

Session session = CriadorDeSession.getSession();

		// carrega o produto do banco de dados
		Produto produto = (Produto) session.load(Produto.class, 2L);

		produto.setPreco(42.50);

		//ProdutoDAO dao = new ProdutoDao();
		new ProdutoDao().altera(produto);

	}

[color=red]Erro[/color]:

log4j:WARN No appenders could be found for logger (org.hibernate.type.BasicTypeRegistry). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. Hibernate: select produto0_.id as id0_0_, produto0_.descricao as descricao0_0_, produto0_.nome as nome0_0_, produto0_.preco as preco0_0_ from Produto produto0_ where produto0_.id=? Exception in thread "main" org.hibernate.HibernateException: illegally attempted to associate a proxy with two open Sessions at org.hibernate.proxy.AbstractLazyInitializer.setSession(AbstractLazyInitializer.java:126) at org.hibernate.engine.StatefulPersistenceContext.reassociateProxy(StatefulPersistenceContext.java:573) at org.hibernate.engine.StatefulPersistenceContext.unproxyAndReassociate(StatefulPersistenceContext.java:618) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89) at org.hibernate.impl.SessionImpl.fireUpdate(SessionImpl.java:742) at org.hibernate.impl.SessionImpl.update(SessionImpl.java:730) at org.hibernate.impl.SessionImpl.update(SessionImpl.java:722) at br.com.caelum.goodbuy.dao.ProdutoDao.altera(ProdutoDao.java:27) at br.com.caelum.goodbuy.dao.AlteracaoDeProduto.main(AlteracaoDeProduto.java:24)

RemoveProduto:

package br.com.caelum.goodbuy.dao;

import org.hibernate.Session;

import br.com.caelum.goodbuy.infra.CriadorDeSession;
import br.com.caelum.goodbuy.modelo.Produto;

public class RemocaoDeProduto {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Session session = CriadorDeSession.getSession();

		Produto produto =  (Produto) session.load(Produto.class, 2L);

		new ProdutoDao().remove( produto);

	}

	

}

Erro:

log4j:WARN No appenders could be found for logger (org.hibernate.type.BasicTypeRegistry).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Exception in thread "main" org.hibernate.HibernateException: illegally attempted to associate a proxy with two open Sessions
	at org.hibernate.proxy.AbstractLazyInitializer.setSession(AbstractLazyInitializer.java:126)
	at org.hibernate.engine.StatefulPersistenceContext.reassociateProxy(StatefulPersistenceContext.java:573)
	at org.hibernate.engine.StatefulPersistenceContext.unproxyAndReassociate(StatefulPersistenceContext.java:618)
	at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:89)
	at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:73)
	at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:956)
	at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:934)
	at br.com.caelum.goodbuy.dao.ProdutoDao.remove(ProdutoDao.java:33)
	at br.com.caelum.goodbuy.dao.RemocaoDeProduto.main(RemocaoDeProduto.java:20)

Alguém me ajuda neste erro por favor?

         Session session = CriadorDeSession.getSession();   //aqui vc cria uma session e faz a busca e logo em seguida faz um update 
  
        // carrega o produto do banco de dados  
        Produto produto = (Produto) session.load(Produto.class, 2L);  
  
        produto.setPreco(42.50);  
  
        //ProdutoDAO dao = new ProdutoDao(); 
        new ProdutoDao().altera(produto);  // aqui nesse construtor vc cria uma nova session
  
    }  

tenta fechar a primeira session antes de altera o produto

org.hibernate.HibernateException: illegally attempted to associate a proxy with two open Sessions  

outra coisa porque em vez de vc fazer

Session session = CriadorDeSession.getSession();  
  
        Produto produto =  (Produto) session.load(Produto.class, 2L);  
  
        new ProdutoDao().remove( produto); 

vc não faz isso


Produto dao = new ProdutoDao();
Produto produto = dao.busca(2L); 
 dao.remove( produto); 

e no teste de alteração faz a mesma coisa


Produto dao = new ProdutoDao();
Produto produto = dao.busca(2L); 

produto.setPreco(42.50);  
  
        
dao.altera(produto); 

fiz a busca assim:

public Produto busca(long id){ return (Produto) session.load(Produto.class, id); }

Daí ficou tudo mais fácil, só chamar a busca primeiro e depois a alteração e remoção;

Agora eu pergunto o código responsável por fechar o banco de dados, já tem no hibernate implementado? tenho que me preocupar com isso?
Obrigado Danilo pela ajuda te agradeço + 1 vez!

o ideal é quem abre a conexão tambem fecha-la mas no seu caso se vc fizer isso seu DAO terá responsabilidades que não deveria ser dele (abrir e fechar conexões).

pra que vc tenha uma classe mais coesa o interessante é vc inverte o controle, em vez de seu DAO criar a session ele receber uma session, ai vc me pergunta, como fazer isso?

simples em vez de instanciar uma session no construtor, receber um session no construtor

ex:


public class ProdutoDao {  
    private final Session session;  
  
    public ProdutoDao(Session session) {  
        this.session = session; 
    }  
  
    public void salva(Produto produto) {  
  
        Transaction tx = session.beginTransaction();  
        session.save(produto);  
        tx.commit();  
    }  
  
    public void altera(Produto produto) {  
        Transaction tx = session.beginTransaction();  
        session.update(produto);  
        tx.commit();  
    }  
  
    public void remove(Produto produto) {  
        Transaction tx = session.beginTransaction();  
        session.delete(produto);  
        tx.commit();  
    }  
  
    public List<Produto> listaTudo() {  
        return this.session.createCriteria(Produto.class).list();  
    }  
  
}  

no seu teste agora ficaria assim

Session session = CriadorDeSession.getSession();
ProdutoDao dao = new ProdutoDao(session);
Produto produto = dao.busca(2L);
session.close(); 

com isso seu DAO fica mais coeso perdendo assim a responsabilidade de criar e fechar as sessions

é como na fj21 ne que deopis se utilzia o criteria!
Obrigado Danilo!

Eu modifiquei para assim, sendo que dessa forma me causu um erro quando fui listar toda a lista de Produtos no arquivo ListaTudo.jsp, que antes me listava os produtos e depois dessa modificação não me funciona.
Bem antes o construtor de meu ProdutoDAO estava assim:

public ProdutoDao() {  
        this.session = CriadorDeSession.getSession();  
    }  

e meu arquivo listaTudo.jsp está:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<h1>- Supermercado BomPreço -</h1>
</br>
	Lista de Produtos:
	<table border="1">
		<!-- for -->
		<tr>
			<td>ID</td>
			<td>Descricao</td>
			<td>Nome</td>
			<td>Preco</td>
		</tr>
		<c:forEach var="produto" items="${produtoList}">			
			<tr>
				<td>${produto.id}</td>
				<td>${produto.descricao}</td>
				<td>${produto.nome}</td>
				<td>${produto.preco}</td>
			</tr>	
		</c:forEach>
	</table>

e me funcionava antes e me listava todos os produtos utilizando o taglib JSTL. Sendo com a modificação nao me vai mais, eu devo chamar a session aonde agora depois com essa modificação, eu estou pensando em voltar para a maneira anterior que me funcionava e depois no final so colocaria para fechar a session já no final de cada método!
o que achariam melhor?

E outra dúvida para mostrar somente um Produto eu estou tentando mostrar o Produto através do método busca, mas não me sai nada, me dá um erro, então pergunto, eu teria que fazer um outro método em ProdutoDAO para imprimir o listado de um produto que fiz a busca ou com o método busca já me valeria para eu para eu imprimir os dados de um produto? porque se for no caso de utilizar somente meu método busca da classe ProdutoDAO eu teria que passar o ID como parametro para me retornar a busca do produto, mas daí nessa hora que eu vejo que precisaria de uma lista, para poder mostrar os valores como produto.nome, produto.preco, etc…

[color=red]ERRO:[/color]

vc tem que construir um provider que vai injetar a session pra vc ou usar o provider que ja vem como o vraptor adicionando isso :

 <context-param>
    <param-name>br.com.caelum.vraptor.provider</param-name>
    <param-value>br.com.caelum.vraptor.util.hibernate.HibernateCustomProvider</param-value>
  </context-param>

no web.xml

na apostila vai ensinar como fazer, por enquanto vc pode deixar como estava antes criando a session no construtor do dao

blz isso mesmo, vou retornar que posteriormente ele modificará neh…
Mas sobre a dúvida de mostrar somente um produto, terei que passar para uma lista neh??

eu não entendi muito bem sua duvida de mostra somente um produto

e que eu estava fazendo os exercicios Opcionais, daí tem que pede:
[list]8) (Opcional) Crie outro método que retorne um Produto, com algumas informações preenchidas. Tente imprimir essas informações no jsp.[/list]
Eu estava querendo mostrar os dados do Produto, sendo que para utilizar o metodo busca ele pede que envie como parametro o ID do produto, como por exemplo meu codigo esta assim:

public Produto busca(Long id){ return (Produto) session.load(Produto.class, id); }

Daí eu penso que para mostrar os valores de algum produo que seja eu teria que fazer uma lista, mesmo que seja um produto só… Mas percebi que o exercicio é outra coisa, já vejo que é para fazer tipo que fazemos com alteração na base de dados e temos que popular os campos, daí é outra coisa, sendo que adiantei e já vejo que terei que fazer o Controller…

entendi,
no caso de vc esta na lista vc pode chamar o controler passando o id do produto, ai vc vai usar esse metodo buscar passsando o id