Erro salvando objetos com relacionamento

3 respostas
kvnallen

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

@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

Traduzindo, ele tem uma lista de produtos.

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

@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

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

Código que fiz:

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);
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(p) já que ai eu seto em Produto o fornecedor
p.setFornecedor(f);
mas ele sempre dá nullpointer.

Alguem consegue me ajudar ? :(

3 Respostas

adornes

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  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,  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!

kvnallen

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:

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);

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

adornes

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

Abraço

Criado 6 de maio de 2012
Ultima resposta 7 de mai. de 2012
Respostas 3
Participantes 2