Erro salvando objetos com relacionamento

Opa pessoal, estou desenvolvendo um software de pizzaria que empaquei em uns relacionamentos.
É o seguinte, tenho a classe Fornecedor que tem as variáveis

[code]@Entity
@Table(name=“tbl_fornecedor”)
public class Fornecedor {

@Id
@GeneratedValue
@Column(name="id_fornecedor")
private long id;

@Column(name = "nome", nullable = false, unique=true , length = 50)
private String nome;

@Column(name = "telefone", length = 20)
private String telefone;

@Column(name = "endereco", length = 70)
private String endereco;

@Column(name = "numero", length = 6)
private String numero;

@Column(name = "bairro", length = 20)
private String bairro;

@Column(name = "conta", length = 30)
private String conta;

@Column(name = "banco", length = 30)
private String banco;

@Column(name = "cnpj", length = 14)
private String cnpj;

@Column(name = "email", length = 40)
private String email;

@OneToMany(mappedBy="fornecedor", fetch=FetchType.EAGER)
@Cascade(CascadeType.ALL)
private List<Produto> produtos;

//Getters e Setters[/code]

Traduzindo, ele tem uma lista de produtos.

E tenho a classe produtos, que cada produto tem um fornecedor

[code]@Entity
@Table(name = “tbl_produto”)
public class Produto {

@Id
@GeneratedValue
@Column(name = "id_produto")
private long id;

@Column(name = "nome", nullable = false, length = 50)
private String nome;

@Column(name = "preco_unitario", length = 10, nullable = false)
private double precoUnitario;

@Column(name = "preco_venda", length = 10, nullable = false)
private double precoVenda;

@Column(name = "unidade_medida", length = 10, nullable=false)
private String unidadeMedida;

@Column(name = "quantidade_disponivel", length = 20, nullable=false)
private int quantidadeDisponivel;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_fornecedor", insertable = true, updatable = true)
@Fetch(FetchMode.JOIN)
@Cascade(CascadeType.SAVE_UPDATE)
private Fornecedor fornecedor;

@Column(name = "tipo", length=10)
private String tipo;

//Getters e Setters[/code]

Então, consegui salvar aqui pra relacionar e tal, mas eu só consigo salvando pelo fornecedor, estou utilizando Hibernate.

Código que fiz:

[code] Fachada fachada = Fachada.obterInstancia();
Produto p = new Produto();
Fornecedor f = new Fornecedor();

	//Produto 1
	p.setNome("Cervejinha teste");
	p.setPrecoUnitario(2);
	p.setPrecoVenda(3);
	p.setQuantidadeDisponivel(1);
	p.setTipo("Bebida");
	p.setUnidadeMedida("g");
	
	//Seta o fornecedor
	f.setId(4);
	f.setNome("rs");
	
	
	
	//Seta o produto no fornecedor
	List<Produto> produtos = new ArrayList<Produto>();
	produtos.add(p);
	
            //Seta a lista de produtos no fornecedor, e o fornecedor nos produtos.
            f.setProdutos(produtos);
	p.setFornecedor(f);
	
	
	//Salva o Produto
	fachada.atualizarFornecedor(f);[/code]

Sendo que só consegui salvar o produto atualizando o fornecedor, e o problema que ele não deixa eu atualizar o fornecedor com o mesmo nome, prai eu teria que dar um update com o nome diferente, depois dar outro update com o nome que estava, e não quero fazer essa gambiarra, tentei salvar o produto pelo fachada.salvarProduto§ já que ai eu seto em Produto o fornecedor p.setFornecedor(f); mas ele sempre dá nullpointer.

Alguem consegue me ajudar ? :frowning:

Ae kvnallen,

Analisando teu código, me parece que você acrescentou algumas complexidades talvez desnecessárias e isso sempre deixa o código um tanto mais susceptível a erros.

  • FetchType.EAGER - Só use quando for extremamente necessário, pois essa configuração vai forçar um carregamento em memória que pode afetar grandemente a performance do sistema e, em geral, no é tão necessário. Recomendo que você busque a documentação do Hibernate e compreenda a fundo a diferença de Lazy e Eager para usar da melhor forma estes recursos.

  • insertable, updatable, cascade, também são configurações que, por padrão, já atendem as necessidades gerais de qualquer aplicação e só precisam ser especificadas se for realmente necessário.

  • NullPointerException - É um erro feio, mas o console sempre (sempre mesmo) vai indicar exatamente em que linha deu um NullPointer, e aí você sabe exatamente qual objeto (ou método) está retornando null.

Não testei, mas acho que um código assim iria funcionar normalmente:

 @Entity  
 @Table(name="tbl_fornecedor")  
 public class Fornecedor {  
   
     @Id  
     @GeneratedValue  
     @Column(name="id_fornecedor")  
     private long id;  
   
     @Column(name = "nome", nullable = false, unique=true , length = 50)  
     private String nome;  
   
     @Column(name = "telefone", length = 20)  
     private String telefone;  
   
     @Column(name = "endereco", length = 70)  
     private String endereco;  
   
     @Column(name = "numero", length = 6)  
     private String numero;  
   
     @Column(name = "bairro", length = 20)  
     private String bairro;  
   
     @Column(name = "conta", length = 30)  
     private String conta;  
   
     @Column(name = "banco", length = 30)  
     private String banco;  
   
     @Column(name = "cnpj", length = 14)  
     private String cnpj;  
   
     @Column(name = "email", length = 40)  
     private String email;  
       
     @OneToMany(mappedBy="fornecedor")  
     private List<Produto> produtos = new ArrayList<Produto>();  //Tenho esse costume de já deixar uma lista instanciada por padrão para evitar nullpointer  
       
     //Getters e Setters
 @Entity  
 @Table(name = "tbl_produto")  
 public class Produto {  
   
     @Id  
     @GeneratedValue  
     @Column(name = "id_produto")  
     private long id;  
   
     @Column(name = "nome", nullable = false, length = 50)  
     private String nome;  
   
     @Column(name = "preco_unitario", length = 10, nullable = false)  
     private double precoUnitario;  
   
     @Column(name = "preco_venda", length = 10, nullable = false)  
     private double precoVenda;  
   
     @Column(name = "unidade_medida", length = 10, nullable=false)  
     private String unidadeMedida;  
   
     @Column(name = "quantidade_disponivel", length = 20, nullable=false)  
     private int quantidadeDisponivel;  
   
     @ManyToOne
     @JoinColumn(name = "id_fornecedor")  
     private Fornecedor fornecedor;  
   
     @Column(name = "tipo", length=10)  
     private String tipo;  
   
     //Getters e Setters

E

     Fachada fachada = Fachada.obterInstancia();  
     Produto p = new Produto();  
     Fornecedor f = new Fornecedor();
       
     //Seta o fornecedor  
     //f.setId(4);   // O ID será gerado automaticamente conforme Annotation @GeneratedValue. Inclusive este setter não precisa existir no pojo.1
     f.setNome("rs");

     fachada.atualizarFornecedor(f);  //Não sei o que esse método faz, mas nesta parte do código esse fornecedor deve ser inserido e não atualizado, pois trata-se de um registro novo e não um já existente
       
     //Produto 1  
     p.setNome("Cervejinha teste");  
     p.setPrecoUnitario(2);  
     p.setPrecoVenda(3);  
     p.setQuantidadeDisponivel(1);  
     p.setTipo("Bebida");  
     p.setUnidadeMedida("g");
     p.setFornecedor(f);
             
                //Seta a lista de produtos no fornecedor, e o fornecedor nos produtos.  
                f.setProdutos(produtos);  
     p.setFornecedor(f);  
       
     //Salva o Produto  
     fachada.atualizarProduto(p); // Mesma observação que fiz com o Fornecedor. Este produto é novo e deve ser inserido e não atualizado.
     

Neste ponto, tudo deve estar salvo corretamente, exceto se a implementação do Fachada estiver bugada. Se vc chamar f.getProdutos() o seu produto não estará lá, pq não foi adicionado em memória. Mas se você recuperar o f do banco (tipo um fachada.buscaFornecedor(id)) aí o produto já estará lá!!

Uma alternativa também seria adicionar o produto na lista e salvar somente o fornecedor (sem precisar salvar o produto).

f.getProdutos().add(p); //Não vai dar NullPointer pq, mesmo que não tenha nenhum produto ainda, já tem a lista padrão instanciada.
fachada.salvarFornecedor(f);

Acho que é isso. Faz uns testes aí e vê se agora funciona. Mas, em geral, o mais importante é conhecer bem essas Annotations do Hibernate (JPA) e utilizá-las adequadamente, aí tem tudo para funcionar redondinho.

Depois diz aí se funcionou.

Forte abraço!

Entendi, obrigado!
Tipo, o que eu estava tendo problema mesmo é tipo assim
Digamos que o fornecedor já está cadastrado, então eu queria inserir um produto ex: Trigo, nesse produto o seu fornecedor é o josé que tem o ID 1.

Então, meu código na fachada de atualizar é necessário passar um objeto com o ID, então por isso eu fiz o f.setId(4); para poder atualizar o objeto com ID 4 e setar o produto nele.
---------------------EDIT ------------------------------------------------------

Consegui!
Meu código para inserir um novo produto já existindo um fornecedor ficou assim:

[code] Fachada fachada = Fachada.obterInstancia();
Produto p = new Produto();
Fornecedor f = new Fornecedor();

//Seta o id do fornecedor do produto
f.setId(1);

//Produto 1
p.setNome(“Cervejinha teste”);
p.setPrecoUnitario(2);
p.setPrecoVenda(3);
p.setQuantidadeDisponivel(1);
p.setTipo(“Bebida”);
p.setUnidadeMedida(“g”);
p.setFornecedor(f);

     List<Produto> produtos = new ArrayList<Produto>();
     produtos.add(p);

//Seta a lista de produtos no fornecedor, e o fornecedor nos produtos.
f.setProdutos(produtos);
p.setFornecedor(f);

     //Salva o Produto  
     fachada.salvarProduto(p);

[/code]

Só uma dúvida, tem algum modo de quando eu remover um fornecedor, o produto dele também ser removido ? Acho que é algo em @Cascade

Exatamente. Com o Cascate.DELETE ou o CASCADE.ALL que abrange também o DELETE. Isso no atributo List da classe Fornecedor.

Abraço