Solucionado (ver solução)
Solucionado
(ver solução)
7
respostas

Está correto dessa maneira?

Boa madrugada, eu abri esse tópico em off, porque ja abri uma vez um quase igual a esse e com a mesma duvida, e não me responderam com clareza, peço desculpa por tópico quase duplicado, mais não gosto de seguir em duvida, na alura em java não vi nenhum assunto relacionado a isso. Ok vou dar um exemplo, tenho a minha classe produto.

@Entity
@NamedQueries({
    @NamedQuery(name = "Produto.lista", query = "Select p from Produto p join fetch p.fabricantes")
})
public class Produto implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long codigo;

    private String descricao;
    private Double preco;
    @OneToMany(mappedBy = "produto", cascade = CascadeType.REMOVE)
    private List<Fabricante> fabricantes = new ArrayList<Fabricante>();

    public Long getCodigo() {
        return codigo;
    }
    public void setCodigo(Long codigo) {
        this.codigo = codigo;
    }
    public String getDescricao() {
        return descricao;
    }
    public void setDescricao(String descricao) {
        this.descricao = descricao;
    }
    public Double getPreco() {
        return preco;
    }
    public void setPreco(Double preco) {
        this.preco = preco;
    }
    public List<Fabricante> getFabricantes() {
        return fabricantes;
    }
    public void setFabricantes(List<Fabricante> fabricantes) {
        this.fabricantes = fabricantes;
    }
}

Reparando na classe ela tem um atributo List, com uma anotação @OneToMany(mappedBy = "produto", cascade = CascadeType.REMOVE) aqui não sei se estou certo, mais eu queria que excluisse um produto e excluisse todos os fabricantes desse produto, por isso usei o casade = CascadeType.REMOVE, está correto em usar assim? Mais logo depois quando clico para excluir que vem a duvida, sempre quando clico em excluir o seu sql gera um insert assim...

Hibernate: insert into Fabricante (nome, produto_codigo) values (?, ?)
Hibernate: delete from Fabricante where codigo=?
Hibernate: delete from Produto where codigo=?
Hibernate: delete from Fabricante where codigo=?

Como pode ver quando clico em excluir está fazendo um insert junto, ou seja está adicionando algo, e quando excluo o meu id sempre pula 1 no meu banco de dados. Oque estou errando? PF quem responder responde com uma solução :( Irei postar todas as classes aqui já para dar uma olhada.

@Entity
public class Fabricante implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long codigo;
    private String nome;
    @ManyToOne
    @JoinColumn(name = "produto_codigo")
    private Produto produto = new Produto();
public class ProdutoDao implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Inject
    EntityManager manager;


    public void salvar(Produto produto, Fabricante fabricante) {
        manager.persist(produto);
        manager.persist(fabricante);
    }

    @SuppressWarnings("unchecked")
    public List<Produto> listaProdutos() {
        Query query = manager.createNamedQuery("Produto.lista");
        return query.getResultList();
    }

    public void deletar(Produto produto, Fabricante fabricante) {
        manager.remove(manager.merge(produto));
        manager.remove(manager.merge(fabricante));
    }
}
@Named
@ViewScoped
public class ProdutoBean implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private Produto produto = new Produto();
    private Fabricante fabricante = new Fabricante();
    @Inject
    private ProdutoDao produtoDao;
    private List<Produto> produtos;

    @Transacional
    public void salvar() {
        if(produto.getCodigo() == null) {
            fabricante.setProduto(produto);
            produtoDao.salvar(produto, fabricante);
            System.out.println("Produto salvo com sucesso");
            this.produto = new Produto();
            this.fabricante = new Fabricante();
        }
    }

    @Transacional
    public void remover(Produto produto) {
        produtoDao.deletar(produto, fabricante);
    }

    public List<Produto> getLista() {
        return produtos = produtoDao.listaProdutos();
    }

    public Redirecionador formularioProduto() {
        return new Redirecionador("produtoPesquisa");
    }

    public Produto getProduto() {
        return produto;
    }
    public void setProduto(Produto produto) {
        this.produto = produto;
    }
    public Fabricante getFabricante() {
        return fabricante;
    }
    public void setFabricante(Fabricante fabricante) {
        this.fabricante = fabricante;
    }

    public List<Produto> getProdutos() {
        return produtos;
    }

    public void setProdutos(List<Produto> produtos) {
        this.produtos = produtos;
    }


}

E se eu tiro o meu cascade = CascadeType.REMOVE da minha anotação, o erro é esse.

Cannot delete or update a parent row: a foreign key constraint fails (teste.fabricante, CONSTRAINT FK58A41A3A0149504 FOREIGN KEY (produto_codigo) REFERENCES produto (codigo))

7 respostas

Alisson, teu problema deve estar nesse método:

public void deletar(Produto produto, Fabricante fabricante) {
    manager.remove(manager.merge(produto));
    manager.remove(manager.merge(fabricante));
}

Porque você pede pra fazer um merge antes de deletar, daí ele faz um insert se o objeto não estiver no banco (é o que parece estar acontecendo).

Lembre-se que você está programando orientado a objetos, então, todos os objetos do tipo fabricante já estão dentro de um array no objeto produto (embora eu ache que um fabricante deva ter uma lista de produtos... hehe. Depende da modelagem). Então, quando você deleta o produto, automaticamente, está deletando todos os fabricantes "incluídos" nele. Acho até que já te expliquei sobre isso em uma dúvida anterior.. rs.

Quanto ao CascadeType.REMOVE, tive que pesquisar mais sobre ele esses dias pra poder entender o que estava acontecendo que ele não removia os objetos-filhos. Ocorre que se você usar o CascadeType.REMOVE da JPA, ela não vai remover os objetos filhos por um problema na implementação. Pra que ocorra a exclusão direitinho, você precisa usar o CascadeType.REMOVE do Hibernate (o que não é tãããão bom de fazer - misturar especificação com provider, coisa conceitual mesmo, que pode dar problema se você for trocar o framework de persistência algum dia na vida).

No mais, acho que isso já deve te ajudar a resolver essa parada.

Abraço.

Manoel então se eu excluir só o objeto produto já ira excluir o seu fabricante? como por exemplo.

public void deletar(Produto produto) {
    manager.remove(manager.merge(produto));
}

Aqui ja deletaria o produto e seu fabricante? você é a favor da anotação cascade? me parece que não é uma boa coisa, teria como remover sem essas anotações?

Alterei o meu código ficando assim.

public void deletar(Produto produto) {
    manager.remove(produto);
}

E deixei com a anotação cascade do jeito que tava, e agora ta tudo certo. Mais uma vez obrigado parceiro!!! Bom final de semana

solução!

Isso, Alisson! Basta, apenas, remover o objeto pai:

public void deletar(Produto produto) {
    manager.remove(produto);
}

Excluindo o objeto pai, você exclui os filhos. O cascade pode ser usado sim, mas deve ser bem pensado, pois é algo que pode gerar "efeitos colaterais".

Tópico respondido? hehe

Bom domingo, brother.

Abraço.

Sim respondido kkk, oque seria esses efeitos colaterais? teria algum problema em usar a anotação? Teria uma outra forma de deletar então sem usar essa anotação? Um outro tópico que ta aberto e você me passo uma anotação que voçe me disse que é a mesma coisa que o cascade. Então é melhor usar a outra anotação JPA doque o cascade?

Os efeitos são as exclusões de registros que não deveriam ser removidos. Pode parecer que não vai dar problema, mas pode acontecer... kkkkk.

Sobre o outro tópico, aquela anotação serve, mas só nos relacionamentos que especifiquei. Nos demais, terá que ser cascade mesmo. Não há problemas em usar o cascade, apenas do pacote da jpa não vai funcionar, você terá que usar do hibernate.

Ah sim muito boa sua explicação, porque não vira moderador daqui? kkk abraço e mais uma vez muito obrigado!! sério