2010-07-08 14 views
7

Tengo una relación básica de uno a muchos parental/infantil en el capítulo 21 del libro de referencias de Hibernate.
La cascada solo es de padres a hijos (persistir en cascada solo porque no quiero eliminar el padre si elimino un hijo).
Cuando agrego un niño al padre y ahorro del niño, tengo una TransientObjectException ...Hibernate - Una relación entre muchas y orphanRemoval cascade

@Entity 
public class Parent implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 

    @OneToMany(mappedBy = "parent", orphanRemoval = true) 
    private List<Child> childs; 

    public List<Child> getChilds() { 
    return childs; 
    } 

    public void setChilds(List<Child> childs) { 
    this.childs = childs; 
    } 

    public void addChild(Child child) { 
    if (childs == null) childs = new ArrayList<Child>(); 
    if (childs.add(child)) child.setParent(this); 
    } 

    public Long getId() { 
    return id; 
    } 

    public void setId(Long id) { 
    this.id = id; 
    } 
} 

@Entity 
public class Child implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 

    @ManyToOne(optional = false) 
    @Cascade({ PERSIST, MERGE, REFRESH, SAVE_UPDATE, REPLICATE, LOCK, DETACH }) 
    private Parent parent; 

    public Parent getParent() { 
    return parent; 
    } 

    public void setParent(Parent parent) { 
    this.parent = parent; 
    } 

    public Long getId() { 
    return id; 
    } 

    public void setId(Long id) { 
    this.id = id; 
    } 
} 


@Test 
public void test() { 
    Parent parent = new Parent(); 
    Child child = new Child(); 
    parent.addChild(child); 
    genericDao.saveOrUpdate(child); 
} 

Pero en el saveOrUpdate, tengo esta excepción:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Child 
    at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:244) 
    at org.hibernate.collection.AbstractPersistentCollection.getOrphans(AbstractPersistentCollection.java:911) 
    at org.hibernate.collection.PersistentBag.getOrphans(PersistentBag.java:143) 
    at org.hibernate.engine.CollectionEntry.getOrphans(CollectionEntry.java:373) 
    at org.hibernate.engine.Cascade.deleteOrphans(Cascade.java:471) 
    at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:455) 
    at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:362) 
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:338) 
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204) 
    at org.hibernate.engine.Cascade.cascade(Cascade.java:161) 
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:476) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:354) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204) 
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) 
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677) 
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669) 
    at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:252) 
    at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392) 
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335) 
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204) 
    at org.hibernate.engine.Cascade.cascade(Cascade.java:161) 
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:451) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288) 
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204) 
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117) 
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) 
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677) 
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669) 
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:665) 

Realmente Don No entiendo porque al guardar al Niño debe guardar al Padre a través de la cascada ... ¿Alguna idea?

ACTUALIZA 1
El problema parece estar relacionado con "orphanRemoval" porque si les comento que en la matriz:

@OneToMany(mappedBy = "parent" /*, orphanRemoval = true */) 
private List<Child> childs; 

funciona!
Guarda al niño, luego al padre.
Pero realmente necesito que se elimine el huérfano a través de la cascada cuando elimino un hijo de su padre.

ACTUALIZACIÓN 2
He creado un problema de JIRA:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-5364

ACTUALIZACIÓN 3
Parece ser fijado :-)
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2269

+0

Bienvenido al desbordamiento de la pila! Use el botón con ceros y unos para formatear correctamente su código en el futuro (lo formateé para usted). –

+0

Gracias ... lo hicimos al mismo tiempo ;-) –

+0

¿Qué sucede si guarda el padre antes de guardar al niño? – Kendrick

Respuesta

0

Básicamente se están violando una restricción. La fila en la base de datos que corresponde a la matriz no existe, por lo que no existe una relación de clave externa que el niño pueda usar para referirse a la matriz. Agregue una llamada a saveOrUpdate en el padre antes de hacer hacer para el niño.

(editar) Me perdí tu comentario sobre la cascada antes del reformateo. Mi recuerdo es que la cascada no funciona en sentido ascendente de esa manera; aún necesitaría guardar al padre primero.

+0

Bien, pero saveOrUpdate se ejecuta en una sola transacción, por lo que primero guardará el elemento secundario, luego la cascada creará el elemento primario (y actualizará la clave externa hija) y finalmente se va a DB y solo en este momento las restricciones se validarán. . no ? –

+0

intenté agregar 'inverse = true' al mapeo padre? No creo que eso funcione, pero Hibernate me sorprende. – Mikeb

+0

No estoy utilizando la configuración xml, pero las anotaciones. @OneToMany (mappedBy = "parent") hace lo mismo. –

Cuestiones relacionadas