JPA OneToMany não excluindo filho

Eu tenho um problema com um simples mapeamento @OneToMany entre um pai e uma entidade filho. Tudo funciona bem, apenas que os registros filho não são excluídos quando eu removê-los da coleção.

O pai:

 @Entity public class Parent { @Id @Column(name = "ID") private Long id; @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent") private Set childs = new HashSet(); ... } 

A criança:

 @Entity public class Child { @Id @Column(name = "ID") private Long id; @ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name="PARENTID", nullable = false) private Parent parent; ... } 

Se eu excluir agora e filho do conjunto de filhos, ele não será excluído do database. Eu tentei anular a referência child.parent , mas isso também não funcionou.

As entidades são usadas em um aplicativo da web, a exclusão acontece como parte de uma solicitação do Ajax. Eu não tenho uma lista de childs deletados quando o botão save é pressionado, então não posso deletá-los implicitamente.

O comportamento do JPA é correto (ou seja, conforme a especificação ): os objects não são excluídos simplesmente porque você os removeu de uma coleção OneToMany. Existem extensões específicas do fornecedor que fazem isso, mas o JPA nativo não cuida disso.

Em parte, isso ocorre porque o JPA não sabe realmente se deve excluir algo removido da coleção. Em termos de modelagem de objects, essa é a diferença entre composição e “agregação *.

Na composição , a entidade filha não existe sem o pai. Um exemplo clássico é entre House e Room. Exclua a casa e os quartos também.

A agregação é um tipo de associação mais flexível e é tipificada por Curso e Estudante. Exclua o curso e o aluno ainda existe (provavelmente em outros cursos).

Portanto, você precisa usar extensões específicas do fornecedor para forçar esse comportamento (se disponível) ou excluir explicitamente o filho E removê-lo da coleção do pai.

Estou ciente de:

  • Hibernate : cascata delete_orphan. Veja 10.11. Persistência transitiva ; e
  • EclipseLink : chama isso de “propriedade privada”. Veja como usar a anotação @PrivateOwned .

Além da resposta de cletus, a JPA 2.0 , final desde dezembro de 2010, introduz um atributo @OneToMany em annotations @OneToMany . Para mais detalhes, veja esta input do blog .

Observe que, como a especificação é relativamente nova, nem todo provedor JPA 1 possui uma implementação final do JPA 2. Por exemplo, o lançamento do Hibernate 3.5.0-Beta-2 ainda não suporta este atributo.

Você pode tentar isto:

@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true) .

Como explicado, não é possível fazer o que eu quero com o JPA, então eu usei a anotação hibernate.cascade, com isso, o código relevante na class Parent agora se parece com isto:

 @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, mappedBy = "parent") @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE, org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.PERSIST, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}) private Set childs = new HashSet(); 

Eu não poderia usar simples ‘ALL’ como isso teria eliminado o pai também.

Aqui cascata, no contexto de remover, significa que as crianças são removidas se você remover o pai. Não a associação. Se você estiver usando o Hibernate como seu provedor JPA, poderá fazê-lo usando a cascata específica do hibernate .

 @Entity class Employee { @OneToOne(orphanRemoval=true) private Address address; } 

Veja aqui

Você pode tentar isto:

 @OneToOne(cascade = CascadeType.REFRESH) 

ou

 @OneToMany(cascade = CascadeType.REFRESH)