Resolvido: VRaptor + Hibernate - Update de Classe [RESOLVIDO]

6 respostas
robertouba

Salve salve…
conforme me indicaram (@danielkist via Twitter) estou abrindo esse post…
Bom, primeiramente não domino nada de VRaptor, mas, o que me acontece é muito maluco!

Vamos lá…
Tenho uma classe usuário, que tem uma senha, por exemplo… e o endereço!

@Entity
public class Usuario {
	@Id
	@GeneratedValue
	private Long id;

	@NotEmpty
	@NotNull
	private String nome;

	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	private Enderecos endereco;

Bom, na hora re inserir eles em um banco, meu procedimento foi primeiramente criar um campo usuario.endereco.id, usuario.endereco.cep e etc…
não rolou na insersão… então coloquei mesmo os campos, criei o endereço setei ele para o usuário e estava feliz assim…
Na hora do update o negócio foi muito macabro!

usuario.id
usuario.nome
usuario.endereco.id
usuario.endereco.cep

etc…
tudo lindo, mas no meu dao.update(usuario) … ele criava novo endereço! kkkkkkkkkkkkkkkkkkkkkkkkkk
pior ainda, se no meu form eu tiver apenas usuario.senha (para trocar a senha do usuario) sem ter mais nenhum campo de usuario.endereco.id ele setava NULL no banco de dados para o endereço!..

Qual o procedimento!?
Tenho visto muitas coisas de Vraptor na net e meu, muito bom, mas, as falta muita coisa ainda, e lógico, um frame desse vai se espalhar muito rápido, entretanto algumas dúvidas pequenas para quem está começando permancessem…

6 Respostas

yorgan

Opa,
Vamos começar.
Primeiro isso é uma questão do Hibernate e não do VRaptor.
Eu acredito que você esteja enviando a entidade para ser atualizada sem informar o ID.
Ou caso esteja enviando o ID, você não está definindo os valores antigos nela para que apenas os campos do form seja atualizado.
De qualquer forma, para ter certeza disso o ideal seria você colocar o código do JSP, do controller e do DAO.

[]'s
Daniel

robertouba

Vamos lá, Usuario DAO

@Component
public class UsuarioDao {
	private final Session session;

	public UsuarioDao(Session session) {
		this.session = session;
	}

	public void add(Usuario u, Enderecos enderecos) {
		Transaction tx = session.beginTransaction();
		String senha = Senhas.getSenha();
		
		u.setSenha(Senhas.criptografar(senha));
		
		session.save(enderecos);
		u.setEndereco(enderecos);
		session.save(u);
		tx.commit();
	}
	
	public void add(Usuario u) {
		Transaction tx = session.beginTransaction();
                session.save(u);
		tx.commit();
	}

	public boolean existeUsuario(Usuario usuario) {
		Usuario encontrado = (Usuario) session.createCriteria(Usuario.class)
				.add(Restrictions.eq("email", usuario.getEmail()))
				.uniqueResult();
		return encontrado != null;
	}

	public void edit(Usuario u) {
		Transaction tx = session.beginTransaction();
		System.out.println("Editar " + u.getId());
		session.update(u);
		tx.commit();
	}
	
	
	@SuppressWarnings("unchecked")
	public List<Usuario> listar() {
		return this.session.createCriteria(Usuario.class).list();
	}

	public Usuario buscar(Long id) {
		return (Usuario) session.load(Usuario.class, id);
	}

	public Usuario carrega(Usuario usuario) {
		return (Usuario) session.createCriteria(Usuario.class).add(
				Restrictions.eq("email", usuario.getEmail())).add(
				Restrictions.eq("senha", Senhas.criptografar(usuario.getSenha()))).uniqueResult();
	}

	


}

Usuario Controller

@Resource
public class UsuarioController {
	private final UsuarioDao dao;
	private final Result result;
	private final Validator validator;
	private final UsuarioWeb usuarioWeb;

	public UsuarioController(UsuarioDao dao, Result result,
			Validator validator, UsuarioWeb usuarioWeb) {
		this.dao = dao;
		this.result = result;
		this.validator = validator;
		this.usuarioWeb = usuarioWeb;
	}

		
	public Usuario edita(Long id) {
		return dao.buscar(id);
	}

	public void altera(Usuario usuario) {
		dao.edit(usuario);
		result.redirectTo(UsuarioController.class).lista();
	}

	
	public void adiciona(final Usuario usuario, final Enderecos enderecos) {
		dao.add(usuario, enderecos);
		result.redirectTo(UsuarioController.class).lista();
	}


}

Agora meu Form… o de adicionar

<form action="<c:url value="/usuarios" />" method="POST">
			<div class="acoes">
				<input type="submit" value="cadastrar">
				<input type="button" value="cancelar" onclick="javascript:history.back(-1);">
			</div>
			<fieldset>
				<legend>Cadastro de Usuario</legend>
				<label><span>Nome</span>
					<input type="text" name="usuario.nome" id="nome" value="${usuario.nome}" />
				</label>
				<label><span>RG</span>
					<input type="text" name="usuario.rg" id="rg" value="${usuario.rg}" />
				</label>
				<label><span>CPF</span>
					<input type="text" name="usuario.cpf" id="cpf" value="${usuario.cpf}" />
				</label>
				<label><span>Carteira Profissional</span>
					<input type="text" name="usuario.clt" id="clt" value="${usuario.clt}" />
				</label>
				<label><span>Nascimento</span>
					<input type="text" name="usuario.dataNascimento" id="clt" value="<fmt:formatDate value="${usuario.dataNascimento}" type="date" pattern="dd/MM/yyyy"/>" />
				</label>
				<label><span>Cargo</span>
					<input type="text" name="usuario.cargo" id="cargo" value="${usuario.cargo}" />
				</label>
				<label><span>E-mail</span>
					<input type="text" name="usuario.email" id="email" value="${usuario.email}" />
				</label>
			</fieldset>
			<fieldset>
				<legend>Endereço</legend>
				<label><span>CEP</span>
					<input type="text" name="enderecos.cep" id="cep" />
				</label>
				<label><span>Endereco</span>
					<input type="text" name="enderecos.endereco" id="endereco" />
				</label>
				<label><span>Numero</span>
					<input type="text" name="enderecos.numero" id="numero" />
				</label>
				<label><span>Complemento</span>
					<input type="text" name="enderecos.comp" id="comp" />
				</label>
				<label><span>Bairro</span>
					<input type="text" name="enderecos.bairro" id="bairro" />
				</label>
				<label><span>Cidade</span>
					<input type="text" name="enderecos.cidade" id="cidade" />
				</label>
				<label><span>UF</span>
					<input type="text" name="enderecos.uf" id="uf" />
				</label>
			</fieldset>
			<div class="acoes">
				<input type="submit" value="cadastrar">
				<input type="button" value="cancelar" onclick="javascript:history.back(-1);">
			</div>

O Form de Editar… Poutsss… Note que eu tive que passar a senha por hidden pq se eu não passasse ele deixava nulo! snifff
A mesma coisa acontecia se eu fizesse um form só com senha! deixava o endereço como nulo!

<form action="<c:url value="/usuarios/${usuario.id}" />" method="POST">
			<input type="hidden" name="_method" value="PUT" />
			<input type="hidden" name="usuario.id" value="${usuario.id}" />
			<input type="hidden" name="usuario.senha" id="senha" value="${usuario.senha}" />
			<input type="hidden" name="usuario.endereco.id" id="senha" value="${usuario.endereco.id}" />
			<div class="acoes">
				<input type="submit" value="atualizar">
				<input type="button" value="cancelar" onclick="javascript:history.back(-1);">
			</div>
			<fieldset>
				<legend>Cadastro de Usuario</legend>
				<label><span>Nome</span>
					<input type="text" name="usuario.nome" id="nome" value="${usuario.nome}" readonly="readonly" />
				</label>
				<label><span>RG</span>
					<input type="text" name="usuario.rg" id="rg" value="${usuario.rg}" readonly="readonly" />
				</label>
				<label><span>CPF</span>
					<input type="text" name="usuario.cpf" id="cpf" value="${usuario.cpf}" readonly="readonly" />
				</label>
				<label><span>Carteira Profissional</span>
					<input type="text" name="usuario.clt" id="clt" value="${usuario.clt}" readonly="readonly" />
				</label>
				<label><span>Nascimento</span>
					<input type="text" name="usuario.dataNascimento" id="clt" value="<fmt:formatDate value="${usuario.dataNascimento}" type="date" pattern="dd/MM/yyyy"/>" />
				</label>
				<label><span>Cargo</span>
					<input type="text" name="usuario.cargo" id="cargo" value="${usuario.cargo}" />
				</label>
				<label><span>E-mail</span>
					<input type="text" name="usuario.email" id="email" value="${usuario.email}" />
				</label>
			</fieldset>
			<fieldset>
				<legend>Endereço</legend>
				<label><span>CEP</span>
					<input type="text" name="usuario.endereco.cep" id="cep" value="${usuario.endereco.cep}" />
				</label>
				<label><span>Endereco</span>
					<input type="text" name="usuario.endereco.endereco" id="endereco" value="${usuario.endereco.endereco}" />
				</label>
				<label><span>Numero</span>
					<input type="text" name="usuario.endereco.numero" id="numero" value="${usuario.endereco.numero}" />
				</label>
				<label><span>Complemento</span>
					<input type="text" name="usuario.endereco.comp" id="comp" value="${usuario.endereco.comp}" />
				</label>
				<label><span>Complemento</span>
					<input type="text" name="usuario.endereco.bairro" id="bairro" value="${usuario.endereco.bairro}" />
				</label>
				<label><span>Cidade</span>
					<input type="text" name="usuario.endereco.cidade" id="cidade" value="${usuario.endereco.cidade}" />
				</label>
				<label><span>UF</span>
					<input type="text" name="usuario.endereco.uf" id="uf" value="${usuario.endereco.uf}" />
				</label>
			</fieldset>
			<div class="acoes">
				<input type="submit" value="atualizar">
				<input type="button" value="cancelar" onclick="javascript:history.back(-1);">
			</div>
		</form>
yorgan

Então, acho que você pode resolver em 2 lugares.
Pode ser no DAO, com algo +/- assim:

public void edit(Usuario u) {
      Usuario usuarioDB = buscar(u); //Aqui ele busca um usuario com os dados atuais;
      Transaction tx = session.beginTransaction();
      System.out.println("Editar " + u.getId());
      usuarioDB.setNome(u.getNome()); //seta o nome que veio do form na entidade do banco.
      session.update(usuarioDB); //atualiza o regitro que veio do banco
      tx.commit();
}

Ou você pode fazer isso no Controller:

public void altera(Usuario usuario) {
      if(usuario.getId() != null) {
            Usuario usuarioDB = dao.buscar(usuario);
            usuarioDB.setNome(usuario.getNome());
            dao.edit(usuarioDB);
      }
      result.redirectTo(UsuarioController.class).lista();
}

Se você fizer no DAO, vai ter que criar um método edit para cada formulário, porque cada formulário manda campos específicos.
Se você fizer no controller, pode deixar o edit como está e em cada método efetuar o procedimento que mostrei acima.

O que está acontecendo no seu caso:
Se você mandar um registro Usuario com apenas alguns campos preenchidos, o Hibernate entende que os demais campos devem ser zerados.
Fazendo da forma com que demonstrei, o hibernate vai comparar os valores e ver que em apenas alguns campos os valores são diferentes e irá atualizar apenas esses campos.

[]'s
Daniel

robertouba

Vamos testar…

robertouba

Bom, acho que o negócio não deu certo, modifiquei minha classe, e está fazendo o update no usuário, mas se eu não colocar o campo do endereço, ele seta o endereço como nulo…
Vou postar as novas classes que fiz…

Usuario DAO…

package br.com.rhfactor.quimiflex.dao;

import java.util.List;

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

import br.com.caelum.vraptor.ioc.Component;
import br.com.rhfactor.quimiflex.entidades.Endereco;
import br.com.rhfactor.quimiflex.entidades.Usuario;

@Component
public class UsuarioDao {
	private final Session session;

	public UsuarioDao(Session session) {
		this.session = session;
	}

	public void add(Usuario usuario, Endereco endereco) {
		Transaction tx = session.beginTransaction();
		session.save(endereco);
		usuario.setEndereco(endereco);
		session.save(usuario);
		tx.commit();
	}

	public void add(Usuario usuario) {
		Transaction tx = session.beginTransaction();
		session.save(usuario);
		tx.commit();
	}

	public void edit(Usuario u) {
		Transaction tx = session.beginTransaction();
		System.out.println("Editar " + u.getId());
		session.update(u);
		tx.commit();
	}

	public void edit(Usuario u, Endereco e) {
		Transaction tx = session.beginTransaction();
		session.update(e);
		session.update(u);
		tx.commit();
	}

	@SuppressWarnings("unchecked")
	public List<Usuario> listar() {
		return this.session.createCriteria(Usuario.class).list();
	}

	public Usuario buscar(Long id) {
		return (Usuario) session.load(Usuario.class, id);
	}

}

Usuario

package br.com.rhfactor.quimiflex.entidades;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;


@Entity
public class Usuario {
	@Id
	@GeneratedValue
	private Long id;
	private String nome;
	@OneToOne()
	private Endereco endereco;

	// Metodos
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public Endereco getEndereco() {
		return endereco;
	}

	public void setEndereco(Endereco endereco) {
		this.endereco = endereco;
	}

}

Endereco

package br.com.rhfactor.quimiflex.dao;

import java.util.List;

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

import br.com.caelum.vraptor.ioc.Component;
import br.com.rhfactor.quimiflex.entidades.Endereco;

@Component
public class EnderecosDao {
	private final Session session;

	public EnderecosDao(Session session) {
		this.session = session;
	}

	public void add(Endereco endereco) {
		Transaction tx = session.beginTransaction();
		session.save(endereco);
		tx.commit();
	}

	@SuppressWarnings("unchecked")
	public List<Endereco> listar() {
		return this.session.createCriteria(Endereco.class).list();
	}

}

Formulario de Edição, quero alterar somente o nome

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Editar Usuario</title>
</head>
<body>
	
	<form method="post" action="<c:url value="/usuario/editar/${usuario.id}" />">
		<input type="hidden" name="_method" value="PUT"/>
		&lt;input type="text" name="usuario.id" value="${usuario.id}" /&gt;<br/>
		
		Nome:&lt;input type="text" name="usuario.nome" value="${usuario.nome}" /&gt;<br/>
		&lt;input type="submit" value="cadastrar" /&gt;
	&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;

Por fim, o controller do Usuario

package br.com.rhfactor.quimiflex.controller;

import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Put;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.rhfactor.quimiflex.dao.UsuarioDao;
import br.com.rhfactor.quimiflex.entidades.Usuario;

@Resource
public class UsuarioController {
	private Result result;
	private UsuarioDao dao;
	
	public UsuarioController(Result result, UsuarioDao dao) {
		this.result = result;
		this.dao = dao;
	}

	public void index(){
	}
	
	public void formulario(){
	}
	
	@Post
	@Path("/usuario/adicionar")
	public void adicionar(final Usuario usuario){
		dao.add(usuario,usuario.getEndereco());
		result.forwardTo(UsuarioController.class).listar();
	}
	
	public void listar(){
		result.include("usuario",dao.listar());
	}
	
	@Get
	@Path("usuario/editar/{id}")
	public Usuario editar(Long id){
		Usuario usuario = dao.buscar(id);
		return usuario;
	}
	
	
	@Put
	@Path("/usuario/editar/{id}")
	public void edita(Usuario usuario){
		dao.edit(usuario);
		result.forwardTo(UsuarioController.class).listar();
	}
	
	
	
}

Por fim, mesmo que eu usasse, como sugerido pelo Daniel…

@Put
	@Path("/usuario/editar/{id}")
	public void edita(Usuario usuario){
		Usuario usuarioDB = dao.buscar(usuario.getId());
		usuarioDB.setNome(usuario.getNome());
		dao.edit(usuarioDB);
		result.forwardTo(UsuarioController.class).listar();
	}

dessa mesma maneira ele perde a referencia dos campos que não estão no formulário, setando como null… ou seja, se eu quisesse um formulário apenas com senha, ele deixaria todos os outros campos como null.

robertouba

Poxa, deu certo sim, apenas tinha me esquecido de reiniciar o Hibernate, e ele já tinha na memória o NULL no campo endereço!
Portanto, deu certinho conforme o Daniel falou…

Carregar o usuario, setar a informação e salvar no banco! Valew Dan!

Criado 26 de maio de 2011
Ultima resposta 26 de mai. de 2011
Respostas 6
Participantes 2