Chave estrangeira atualizada automaticamente [RESOLVIDO]

Olá Pessoal,

Tenho uma classe Usuario e uma classe PerfilUsuario com relação um usuario pode ter muitos perfils
Acontece que quando insiro um novo usuario gostaria que seus perfis fossem adicionados automaticamente, porém estou recebendo um erro dizendo que minha chave estrangeira na tabela perfil não pode ser null. Na verdade ela teria que ser o id do usuario mas infelizmente eu devo estar esquecendo de alguma anotação:
Antes de tudo eu sei que posso inserir o usuario e descobrir o id dele e inserir na tabela perfil (no braço) mas eu gostaria de uma maneira mais automatica na qual tenho certeza que o hibernate consegue fazer e que fosse mais elegante.

main:

colecaoPerfilUsuario.add(perfilUsuario);
usuario.setPerfilUsuario(colecaoPerfilUsuario);
hibernate.salva(usuario);

Usuario

@Entity(name = "tabUsuario")
public class Usuario {
	@Id
	@GeneratedValue
       @Column(name = "idUsuario")
	private int idUsuario;
	@Column(name = "loginUsuario")
	private String login;
	@Column(name = "senhaUsuario")
	private String senha;
	@OneToMany(targetEntity = PerfilUsuario.class, mappedBy = "usuario", cascade = CascadeType.ALL)
	private Collection<PerfilUsuario> perfilUsuario;
	@Column(name = "situacaoUsuario")
	private String situacao;
//ggas

PerfilUsuario

@Entity(name = "tabPerfilUsuario")
public class PerfilUsuario {
	@Id
	@GeneratedValue
	@Column(name = "idPerfilUsuario")
	private int idPerfil;
	@Column(name = "nomePerfilUsuario")
	private String nome;
	@ManyToOne
	@JoinColumn(name = "idUsuario", insertable = true, updatable = true)
	private Usuario usuario;
//ggas

Na tabela usuario tenho:
idUsuario como chave primaria
outros campos como no bean

Na tabela Perfil usuario tenho:
idPerfilUsuario (chave primaria)
idUsuario (chave estrangeira)

Antecipadamente agradeço o tempo desperdiçado na leitura e na possível resposta

cara,

nao seria melhor vc tratar esse seu caso como muitos para muitos, pois como vc disse um usuario pode ter varios perfis e acho que vc um perfil pode ser de varios usuarios.
Caso não seja melhor, poste o log do erro.

t+

Olá amigo,

Obrigado pela dica e realmente vc está certo, muitos pra muitos é mais indicado, assim eu teria que criar uma nova tabela com id do usuario e id do perfil.

Mas mesmo assim gostaria de auxilio para entender minha questão, não só para esse sistema que estou fazendo mas para aprender também e certamente vou precisar disso em algum outro relacionamento…

O log do erro é basicamente: erro ao tentar inserir idUsuario na tabela tabPerfilUsuario campo não pode ser null

Novamente obrigado

colecaoPerfilUsuario.add(perfilUsuario);  
usuario.setPerfilUsuario(colecaoPerfilUsuario);   //acho que o problema é aqui

hibernate.salva(usuario); 

Eu acho que quando você faz um set na coleção o Hibernate perde a referência da coleção persistente. Porque você não tenta assim, cria a coleção dentro da própria entidade e adicione na coleção apenas com getPerfilUsuario().add(perfil). Se eu não me engano, ao fazer a carga da entidade o Hibernate troca a coleção que você criou por uma implementação própria, justamente para manter metadados. Quando você faz o set de uma nova coleção eu acho que essa informação se perde.

segue um exemplo,

@Entity
@Table(name="teste2")
public class Teste2 implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer idteste2;

	private String nome;

	//bi-directional many-to-one association to Teste1
	@OneToMany(mappedBy="teste2", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	private List<Teste1> teste1s;

    public Teste2() {
    	this.teste1s = new ArrayList<Teste1>();
    }
}
@Entity
@Table(name="teste1")
public class Teste1 implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int idteste1;

	private String nome;

	//bi-directional many-to-one association to Teste2
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="teste2")
	private Teste2 teste2;

    public Teste1() {
    }
}
public class Teste {
	public static void main(String[] args) {
		EntityManager entityManager = Persistence.createEntityManagerFactory("bingo").createEntityManager();
		
		Teste2 t2 = new Teste2();
		t2.setNome("t222");
		
		Teste1 t11 = new Teste1();
		t11.setNome("t112");
		t11.setTeste2(t2);
		t2.getTeste1s().add(t11);
		
		Teste1 t12 = new Teste1();
		t12.setNome("t122");
		t12.setTeste2(t2);
		t2.getTeste1s().add(t12);
		
		Teste1 t13 = new Teste1();
		t13.setNome("t132");
		t13.setTeste2(t2);
		t2.getTeste1s().add(t13);
		
		
		entityManager.getTransaction().begin();
		entityManager.persist(t2);
		entityManager.getTransaction().commit();
	}
}

t+

Pessoal,

Mais uma vez muito obrigado, mas…

Mesmo com essas dicas devo estar fazendo algo errado pois continuo recebendo o seguinte erro:

Erro: org.hibernate.exception.GenericJDBCException: could not insert: [br.com.sistaxi.beans.PerfilUsuario]
Hibernate: 
    insert 
    into
        tabUsuario
        (loginUsuario, senhaUsuario, situacaoUsuario) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        tabPerfilUsuario
        (nomePerfilUsuario) 
    values
        (?)
11:00:55  WARN [JDBCExceptionReporter] SQL Error: 1364, SQLState: HY000
11:00:55 ERROR [JDBCExceptionReporter] Field 'idUsuario' doesn't have a default value

Segue abaixo as alterações que fiz:

Usuario:

@Entity(name = "tabUsuario")
public class Usuario {
	@Id
	@GeneratedValue
        @Column(name = "idUsuario")
	private int idUsuario;

	@Column(name = "loginUsuario")
	private String login;

	@Column(name = "senhaUsuario")
	private String senha;

	@OneToMany(targetEntity = PerfilUsuario.class, mappedBy = "usuario", cascade = CascadeType.ALL)
	private List<PerfilUsuario> perfilUsuarioList;

	public Usuario()
	{
		this.setPerfilUsuarioList(new ArrayList<PerfilUsuario>());
	}
//ggas

PerfilUsuario:

@Entity(name = "tabPerfilUsuario")
public class PerfilUsuario {
	@Id
	@GeneratedValue
	@Column(name = "idPerfilUsuario")
	private int idPerfil;

	@Column(name = "nomePerfilUsuario")
	private String nome;

	@ManyToOne
	@JoinColumn(name = "idUsuario", insertable = false, updatable = false)
	private Usuario usuario;
//ggas

main (struts 2):

public class CadastrarEditarUsuarioAction {
	private String mensagem;
	private Hibernate hibernate = new Hibernate();
	private Usuario usuario = new Usuario();
	private PerfilUsuario perfilUsuario = new PerfilUsuario();

	@Action(value = "/cadastrarEditarUsuario", results = {
			@Result(name = "ok", location = "/jsp/menuPage.jsp") })
	
public String execute() {

		        usuario.getPerfilUsuarioList().add(perfilUsuario);
			mensagem = hibernate.salva(usuario);
                        return "ok";
}
//ggas

cara,

vc tem q definir strategy no GeneratedValue de acordo com o seu Banco de Dados.

t+

meu banco é mysql então coloquei (strategy=GenerationType.AUTO) nas chaves primarias dos dois beans GeneratedValue
rodei e continua tudo igual.
A chave primaria está sendo gerada normalmente o que não está sendo salvo é a estrangeira…

Mesmo assim obrigado

estranho cara,

na hora de preencher os objetos vc ta fazendo como eu te passei no exemplo.

pq aqui esse teste ta funcionando perfeito

t+

Estou recebendo os dados por struts 2, fiz o debug para conferir e realmente está tudo chegando de acordo…
O .jsp está assim:

<s:form action="cadastrarEditarUsuario">
		<s:textfield name="usuario.login" label="Usuário" />
		<s:password name="usuario.senha" label="Senha" />
		<s:textfield name="perfilUsuario.nome" label="Perfil" />
		<s:submit value="Cadastrar"/>

vc ta fazendo isso?

perfilUsuario.setUsuario(usuario);

usuario.getPerfilUsuarios().add(perfilUsuario);

t+

faço assim:

perfilUsuario.setUsuario(usuario); usuario.getPerfilUsuarioList().add(perfilUsuario); mensagem = hibernate.salva(usuario);

so por curiosidade,

vc colocou suas chaves primarias no BD como auto incremente?

coloquei

tabUsuario

[code] 1 idUsuario int(11) Não None AUTO_INCREMENT Alterar Eliminar Mais
2 loginUsuario varchar(45) latin1_swedish_ci Não None Alterar Eliminar Mais
3 senhaUsuario varchar(45) latin1_swedish_ci Não None Alterar Eliminar Mais
4 situacaoUsuario varchar(45) latin1_swedish_ci Não None Alterar Eliminar Mais

Ação Nome chave Tipo Único Pacote Coluna Cardinalidade Collation Nulo Cometário
Editar Eliminar PRIMARY BTREE Sim Não idUsuario 1 A

[/code]

tabPerfilUsuario

[code] # Coluna Tipo Collation Atributos Nulo Padrão Extra Ação
1 idPerfilUsuario int(11) Não None AUTO_INCREMENT Alterar Eliminar Mais
2 idUsuario int(11) Não None Alterar Eliminar Mais
3 nomePerfilUsuario varchar(45) latin1_swedish_ci Não None Alterar Eliminar Mais

Ação Nome chave Tipo Único Pacote Coluna Cardinalidade Collation Nulo Cometário
Editar Eliminar PRIMARY BTREE Sim Não idPerfilUsuario 0 A
Editar Eliminar idUsuario BTREE Não Não idUsuario 0 A

[/code]

e a Foreign Key na tabela PerfilUsuario, ta certinha?

Está bem certinha, inclusive se eu fizer o comando:

ele já faz cascade automaticamente e deleta as linhas na tabPerfilUsuario que estavam relacionadas com aquele perfil

da uma olhada nesse link http://stackoverflow.com/questions/804514/hibernate-field-id-doesnt-have-a-default-value

mais um coisa, coloque suas classes implementando Serializable.

t+

Implementei Serializable e li o artigo, mas ele trata sobre auto incremento e não sobre o hibernate não salvar a chave estrangeira.

O meu erro está bem claro, apesar de eu não saber como resolve-lo, o que acontece é que o hibernate não está gerando código para salvar a chave estrangeira, o código que ele está gerando é somente para salvar a coluna nomePerfil, a coluna idPerfil é auto increment, portanto o hibernate não precisa gerar código pois o banco da conta sozinho, a terceira coluna que é a chave estrangeira que é o problema, o hibernate não está gerando codigo com o valor adequado nem o banco está dando conta sozinho, como ele não pode ser null a aplicação capota.

insert into tabPerfilUsuario (nomePerfilUsuario) values (?) 13:13:29 WARN [JDBCExceptionReporter] SQL Error: 1364, SQLState: HY000 13:13:29 ERROR [JDBCExceptionReporter] Field 'idUsuario' doesn't have a default value

Entre o horário do ultimo post e esse passaram umas 5 horas e esse tempo eu fiquei relendo as dicas daqui e outros tutoriais, sei q tem algum erro no meu codigo, e provavelmente um erro simples pois sou iniciante em java…

Obrigado pessoal !!!

cara,

muito estranho isso viu, posta ai como ficou suas classe usuario e perfilUsuario.

t+

ok, vou mandar tudo completo agora pois como sou iniciante pode ser um erro de import ou qquer outra coisa que não percebi…

Usuario:

package br.com.sistaxi.beans;

import java.io.Serializable;
import java.util.ArrayList;
//import java.util.Collection;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity(name = "tabUsuario")
public class Usuario implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name = "idUsuario")
	private int idUsuario;
	@Column(name = "loginUsuario")
	private String login;
	@Column(name = "senhaUsuario")
	private String senha;
	@OneToMany(targetEntity = PerfilUsuario.class, mappedBy = "usuario", cascade = CascadeType.ALL)
	private List<PerfilUsuario> perfilUsuarioList;
	//private Collection<PerfilUsuario> perfilUsuario;
	@Column(name = "situacaoUsuario")
	private String situacao;
	public Usuario()
	{
		this.setPerfilUsuarioList(new ArrayList<PerfilUsuario>());
	}

	public String getLogin() {
		return login;
	}

	public void setLogin(String login) {
		this.login = login;
	}

	public String getSenha() {
		return senha;
	}

	public void setSenha(String senha) {
		this.senha = senha;
	}

//	public Collection<PerfilUsuario> getPerfilUsuario() {
//		return perfilUsuario;
//	}
//
//	public void setPerfilUsuario(Collection<PerfilUsuario> perfilUsuario) {
//		this.perfilUsuario = perfilUsuario;
//	}

	public int getIdUsuario() {
		return idUsuario;
	}

	public void setIdUsuario(int idUsuario) {
		this.idUsuario = idUsuario;
	}

	public String getSituacao() {
		return situacao;
	}

	public void setSituacao(String situacao) {
		this.situacao = situacao;
	}

	public List<PerfilUsuario> getPerfilUsuarioList() {
		return perfilUsuarioList;
	}

	public void setPerfilUsuarioList(List<PerfilUsuario> perfilUsuarioList) {
		this.perfilUsuarioList = perfilUsuarioList;
	}

}

PerfilUsuario:

package br.com.sistaxi.beans;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity(name = "tabPerfilUsuario")
public class PerfilUsuario implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name = "idPerfilUsuario")
	private int idPerfil;
	@Column(name = "nomePerfilUsuario")
	private String nome;
	@ManyToOne
	@JoinColumn(name = "idUsuario", insertable = false, updatable = false)
	private Usuario usuario;
	//xaxixo
	//@Column(name = "idUsuario")
	//private int idUsuario;

	public String getNome() {
		return nome;
	}

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

	public Usuario getUsuario() {
		return usuario;
	}

	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}

	public int getIdPerfil() {
		return idPerfil;
	}

	public void setIdPerfil(int idPerfil) {
		this.idPerfil = idPerfil;
	}

//	public int getIdUsuario() {
//		return idUsuario;
//	}
//
//	public void setIdUsuario(int idUsuario) {
//		this.idUsuario = idUsuario;
//	}

}

CadastrarEditarUsuarioAction:



package br.com.sistaxi.controle.action;

import java.util.ArrayList;
import java.util.Collection;

import org.apache.struts2.convention.annotation.*;

import br.com.sistaxi.beans.PerfilUsuario;
import br.com.sistaxi.beans.Usuario;
import br.com.sistaxi.hibernate.Hibernate;



public class CadastrarEditarUsuarioAction {
	private String mensagem;
	private Hibernate hibernate = new Hibernate();
	private Usuario usuario = new Usuario();
	private String funcao;
	private ArrayList<Usuario> listaUsuario = new ArrayList<Usuario>();
	private PerfilUsuario perfilUsuario = new PerfilUsuario();
	private String mensagem2;
	private Collection<PerfilUsuario> perfilUsuarioCol = new ArrayList<PerfilUsuario>();
	

	@Action(value = "/cadastrarEditarUsuario", results = {
			@Result(name = "goCadastrarUsuarioPage", location = "/jsp/menuPage.jsp"),
			@Result(name = "goListarUsuariosPage", location = "/jsp/listarUsuariosPage.jsp") })
	public String execute() {
		// Cadastra novo Usuario
		// se tudo for digitado corretamente
		if ("".equals(usuario.getLogin()) || "".equals(usuario.getSenha())
				|| "null".equals(usuario.getSituacao())) {
			mensagem = "Cadastro não efetuado! Complete TODOS os campos!";
		} else {
			if ("true".equals(usuario.getSituacao())) {
				usuario.setSituacao("ativo");
			}
			if ("false".equals(usuario.getSituacao())) {
				usuario.setSituacao("inativo");
			}
		}
			//perfilUsuario.setUsuario(usuario);
			//mensagem = hibernate.salva(perfilUsuario);
		
		
		//salvando pelo usuario
		    perfilUsuario.setUsuario(usuario);  
		    usuario.getPerfilUsuarioList().add(perfilUsuario);
			mensagem = hibernate.salva(usuario);
			
			//Sem relacao
			//usuario = hibernate.getUsuarioByLoginSenha(usuario);
			//perfilUsuario.setIdUsuario(usuario.getIdUsuario());
			//mensagem = mensagem + hibernate.salva(perfilUsuario);
			
//			+ " Usuario: "
//					+ usuario.getLogin() + " Senha: " + usuario.getSenha()
//					+ " Situação: " + usuario.getSituacao();
			//Descobre o novo id do usuario salvo
			//usuario = hibernate.getUsuarioByLoginSenha(usuario);
			//salva o perfil do novo usuario
//			perfilUsuario.setIdUsuario((usuario.getId()));
//			mensagem2 = "Mensagem2: " + hibernate.salva(perfilUsuario)
//					+ " Id do perfil: " + perfilUsuario.getId()
//					+ " nome do perfil :" + perfilUsuario.getNome()
//					+ " id do usuario: " + perfilUsuario.getIdUsuario();

		
		if ("atualiza".equals(funcao)) {
			listaUsuario = hibernate.listaUsuario(usuario);
			return "goListarUsuariosPage";
		} else
			return "goCadastrarUsuarioPage";

	}

	// fim do execute()

	// inicio dos getters and setters

	public String getMensagem() {
		return mensagem;
	}

	public void setMensagem(String mensagem) {
		this.mensagem = mensagem;
	}

	public Usuario getUsuario() {
		return usuario;
	}

	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}

	public String getFuncao() {
		return funcao;
	}

	public void setFuncao(String funcao) {
		this.funcao = funcao;
	}

	public ArrayList<Usuario> getListaUsuario() {
		return listaUsuario;
	}

	public void setListaUsuario(ArrayList<Usuario> listaUsuario) {
		this.listaUsuario = listaUsuario;
	}

	public PerfilUsuario getPerfilUsuario() {
		return perfilUsuario;
	}

	public void setPerfilUsuario(PerfilUsuario perfilUsuario) {
		this.perfilUsuario = perfilUsuario;
	}

	public String getMensagem2() {
		return mensagem2;
	}

	public void setMensagem2(String mensagem2) {
		this.mensagem2 = mensagem2;
	}

	public Collection<PerfilUsuario> getPerfilUsuarioCol() {
		return perfilUsuarioCol;
	}

	public void setPerfilUsuarioCol(Collection<PerfilUsuario> perfilUsuarioCol) {
		this.perfilUsuarioCol = perfilUsuarioCol;
	}
}

CadUserPage.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Sistaxi - Cadastro de Usuário</title>
</head>
<body>
	<table>
	<tr>
	<td>
	<%@ include file="/jsp/menuPage.jsp" %> 
	</td>
	</tr>
	<tr>
	<td>
	<h1>Cadastro de Usuario</h1>
	</td>
	</tr>
	<tr>
	<td>
	<s:form action="cadastrarEditarUsuario">
		<s:textfield name="usuario.login" label="Usuário" />
		<s:password name="usuario.senha" label="Senha" />
		<s:textfield name="perfilUsuario.nome" label="Perfil" />
		<s:radio name="usuario.situacao" label="Situação: " list="#{'true':'ativo','false':'inativo'}"  value="true"/>
		<s:submit value="Cadastrar"/>
	</s:form>
	</td>
	</tr>
	<tr>
	<td>
	${mensagem}
	</td>
	</tr>
	<tr>
	<td>
	${mensagem2}<br>
	</td>
	</tr>
	</table>
</body>
</html>