2010-01-06 63 views
131

Tengo un problema con una asignación simple @OneToMany entre una entidad principal y una entidad secundaria. Todo funciona bien, solo que los registros secundarios no se eliminan cuando los elimino de la colección.JPA OneToMany no elimina hijo

El padre:

@Entity 
public class Parent { 
    @Id 
    @Column(name = "ID") 
    private Long id; 

    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent") 
    private Set<Child> childs = new HashSet<Child>(); 

... 
} 

El niño:

@Entity 
public class Child { 
    @Id 
    @Column(name = "ID") 
    private Long id; 

    @ManyToOne(cascade = CascadeType.ALL) 
    @JoinColumn(name="PARENTID", nullable = false) 
    private Parent parent; 

    ... 
} 

Si ahora borro y el niño desde el conjunto del niño, no se borran de la base de datos. Traté de anular la referencia child.parent, pero eso tampoco funcionó.

Las entidades se utilizan en una aplicación web, la eliminación se produce como parte de una solicitud de Ajax. No tengo una lista de hijos eliminados cuando se presiona el botón Guardar, por lo que no puedo eliminarlos implícitamente.

Respuesta

222

El comportamiento de JPA es correcto (es decir, según la especificación): los objetos no se eliminan simplemente porque los ha eliminado de una colección de OneToMany. Existen extensiones específicas de proveedores que hacen eso pero el JPA nativo no lo satisface.

En parte esto se debe a que JPA no sabe realmente si debería eliminar algo eliminado de la colección. En términos de modelado de objetos, esto es la diferencia entre composición y "agregación *.

en composición, la entidad niño no tiene existencia sin el padre. Un ejemplo clásico es de entre casa y la habitación. Eliminar la Cámara y las habitaciones son demasiado ir.

agregación es un tipo más flexible de asociación y está tipificado por curso y alumno. Eliminar el Curso y todavía existe Student (probablemente en otros cursos).

por lo que necesita, ya sea use extensiones específicas del proveedor para fuerce este comportamiento (si está disponible) o elimine explícitamente al niño Y quítelo de la colección del padre.

yo sepa:

+0

gracias a la buena explicación. entonces es como temí (Hice un poco de búsqueda/lectura antes de que preguntara, solo quería estar a salvo). de alguna manera, comienzo a lamentar la decisión de utilizar JPA API y nit Hibernate directamente ... Voy a probar el puntero Chandra Patni y utilizar el tipo de cascada hibernate delete_orphan. – bert

+0

Tengo un tipo de pregunta similar sobre esto. Sería bueno echar un vistazo a este post aquí, por favor? http://stackoverflow.com/questions/4569857/jsf-how-to-update-the-list-after-delete-an-item-of-that-list –

+59

Con JPA 2.0, ahora puede usar la opción orphanRemoval = true – itsadok

2

Aquí, cascada, en el contexto de eliminar, significa que los elementos secundarios se eliminan si quita el elemento primario. No es la asociación Si está utilizando Hibernate como su proveedor de JPA, puede hacerlo usando hibernate specific cascade.

18

Como se ha explicado, no es posible hacer lo que quiera con la APP, así que emplea la anotación hibernate.cascade, con esto, el código correspondiente en la clase padre ahora se ve así:

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

No pude usar simplemente 'TODO', ya que esto también habría eliminado al padre.

64

Además de la respuesta de cletus, JPA 2.0, final desde diciembre de 2010, introduce un atributo orphanRemoval en las anotaciones @OneToMany. Para obtener más detalles, consulte este blog entry.

Tenga en cuenta que, dado que la especificación es relativamente nueva, no todos los proveedores de JPA 1 tienen una implementación final de JPA 2. Por ejemplo, el Hibernate 3.5.0-Beta-2 release aún no es compatible con este atributo.

32

Puede probar esto:

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

+2

Gracias. Pero la pregunta está de regreso de la JPA 1 veces. Y esta opción no estaba disponible antes que. – bert

+7

bueno saber sin embargo, para aquellos de nosotros que buscamos una solución para esto en el JPA2 veces :) – alex440

1

Puede probar esto:

@OneToOne(cascade = CascadeType.REFRESH) 

o

@OneToMany(cascade = CascadeType.REFRESH) 
2
@Entity 
class Employee { 
    @OneToOne(orphanRemoval=true) 
    private Address address; 
} 

Ver here.

Cuestiones relacionadas