JPA OneToMany não exclusão de criança
Pergunta
Eu tenho um problema com um mapeamento @OneToMany
simples entre um pai e uma entidade filho. Tudo funciona bem, só isso registros filho não são apagados quando eu removê-los da coleção.
O parent:
@Entity
public class Parent {
@Id
@Column(name = "ID")
private Long id;
@OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
private Set<Child> childs = new HashSet<Child>();
...
}
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 agora excluir e criança dos criança definido, ele não ficar excluído do banco de dados. Eu tentei anular a referência child.parent
, mas isso não quer trabalhar.
As entidades são usados ??em uma aplicação web, a exclusão acontece como parte de uma solicitação do Ajax. Eu não tenho uma lista de criança apagados quando o botão Salvar é pressionado, então não posso excluí-los implicitamente.
Solução
comportamento da APP está correta (ou seja, de acordo com a especificação ): objetos não são eliminados simplesmente porque você os removeu de uma coleção OneToMany. Existem extensões específicas do fornecedor que fazem isso, mas JPA nativa não atender a isso.
Em parte, isso é porque JPA na verdade não sei se deve excluir algo removido da coleção. Em termos de modelagem de objetos, esta é a diferença entre composição e "agregação *.
Em composição , a entidade filho não tem existência sem o pai. Um exemplo clássico é entre House e quarto. Excluir a Casa e os quartos ir também.
Agregação é um tipo mais flexível de associação e é caracterizada por curso e Student. Excluir o curso eo estudante ainda existe (provavelmente em outros cursos).
Então, você precisa tanto usar extensões específicas do fornecedor para forçar este comportamento (se disponível) ou explicitamente excluir a criança e removê-lo da coleção do pai.
Eu estou ciente de:
- Hibernate : Cascata delete_orphan. Consulte href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/objectstate.html#objectstate-transitive" 10.11. Persistência transitiva ; e
- EclipseLink : chama isso de "propriedade privada". Consulte Como usar a @PrivateOwned Anotação .
Outras dicas
Além de cletus' resposta, JPA 2.0 , final, desde dezembro de 2010 , introduz um atributo orphanRemoval
em anotações @OneToMany
.
Para mais detalhes veja este blogue entrada .
Note que, desde a especificação é relativamente novo, nem todos provedor de JPA 1 tem uma implementação final JPA 2. Por exemplo, o Hibernate-3.5.0-Beta 2 liberação ainda não suporta este atributo.
Você pode tentar o seguinte:
@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true)
.
Como foi explicado, não é possível fazer o que eu quero com JPA, então eu empregada a anotação hibernate.cascade, com isso, o código relevante na classe Parent agora se parece com isso:
@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<Child> childs = new HashSet<Child>();
Eu poderia não uso simples 'ALL', pois isso teria eliminado o pai também.
Aqui Cascade, no contexto de remover, significa que as crianças são removidos se você remover o pai. Não a associação. Se você estiver usando Hibernate como provedor JPA, você pode fazê-lo usando hibernate específica cascata .
Você pode tentar o seguinte:
@OneToOne(cascade = CascadeType.REFRESH)
ou
@OneToMany(cascade = CascadeType.REFRESH)
@Entity
class Employee {
@OneToOne(orphanRemoval=true)
private Address address;
}
Consulte aqui .