Pregunta

Tengo este problema desde hace mucho tiempo, he buscado en la web y SO, y no he encontrado una solución todavía. Espero que me puedas ayudar en eso.

Tengo una relación padre-hijo entre dos entidades como la siguiente:

@Entity
public class Parent {
    // ...

    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
    private Set<Child> children = new HashSet<Child>();

    // ...
}

@Entity
public class Child {
    // ...

    @ManyToOne(fetch = FetchType.LAZY)
    private Parent parent;

    // ...
}

Lo que pasa es que cuando creo un nuevo hijo y lo asigno a un padre, el padre no se actualiza cuando ya está en el caché.

 Parent parent = new Parent();
 em.persist(parent);

 // ...

 Child child = new Child();
 child.setParent(parent);
 em.persist(child);

 parent.getChildren().size(); // returns 0

He intentado usar @PreUpdate para agregar automáticamente el hijo al padre cuando el hijo persiste, pero en el caso cuando tenemos 2 administradores de entidades en 2 subprocesos diferentes (como en JBoss), el problema sigue existiendo, hasta que Llamamos a em.refresh(parent)

Entonces, la pregunta es: ¿existe una manera de eliminar sin problemas el problema y garantizar que parent.getChildren () siempre devuelva la lista actualizada de niños?

¿Fue útil?

Solución

La mayoría de los ORM se comportarán de esta manera.

El objeto en el caché no se actualiza desde la base de datos (una lectura adicional que no es necesaria). También piense en el modelo de objetos y la persistencia como separados. es decir, mantenga su modelo de objeto coherente consigo mismo y no confíe en el mecanismo de persistencia para hacer esto por usted.

Entonces, si quieres que el objeto se agregue a la colección, hazlo en el " setParent " código.

La mejor práctica en este caso es, de hecho, hacer que un lado de la relación haga todo el trabajo y dejar que el otro lado respete. También sugeriría usar el acceso a los campos en lugar del acceso a los métodos, de esa manera puede personalizar los métodos con mayor flexibilidad.

Agregue un método al padre llamado addChild

 public void addChild(Child child) {
    child.setParent0(this);
    getChildren().add(individualNeed);
 }

y luego haga setParent en Child:

public void setParent(Parent parent) {
   parent.addChild(child);
}

setParent0 en Child es la propiedad stter para parent en child.

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

También sugeriría que " getChildren " método devuelve una colección inmutable para que los desarrolladores no usen inadvertidamente este método (aprendí de la manera más difícil en todo esto).

Una cosa más, deberías tener un código de verificación nulo y otras piezas defensivas en el código anterior, lo dejé afuera para mayor claridad.

Otros consejos

Bastante seguro de que tu problema aquí es tu configuración de Cascada.

@Entity
public class Parent {
   // ...

   @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, 
      cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
   @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
   private Set<Child> children = new HashSet<Child>();

   // ...
}

@Entity
public class Child {
    // ...

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
    private Parent parent;

    // ...
}

El uso de esta configuración en cascada persistirá en cascada y se actualizará a los objetos secundarios.

por ejemplo.

Parent parent = new Parent();
em.persist(parent);

// ...

Child child = new Child();
child.setParent(parent);
em.persist(child); //will cascade update to parent

parent.getChildren().size(); // returns 1

o

Parent parent = new Parent();
Child child = new Child();
parent.setChild(parent);
em.persist(parent); //will cascade update to child

child.getParent(); // returns the parent

más información sobre esto se puede encontrar en Hibernate Annotations

Con respecto a su problema con el almacenamiento en caché, este es un problema muy común cuando tiene varias máquinas virtuales que se ejecutan en la misma base de datos con cachés separados. Se llama " deriva de caché " ;.

La mayoría de las implementaciones de caché compatibles con hibernación (ehcache, OSCache y SwarmCache) tienen un caché distribuido integrado que se puede usar para sincronizar los cachés. El caché distribuido, en general, envía mensajes de multidifusión que actualizan el estado del caché. Si realiza un desalojo de caché de segundo nivel con SessionFactory.evict (Class, id), por ejemplo, se enviará un mensaje de invalidación a los otros cachés del clúster, lo que invalidará cualquier otra copia de ese objeto en otros cachés.

Dependiendo de su implementación, la multidifusión puede o no ser aceptable para usted. Si no es así, es posible que deba usar una solución de caché única como memcached.

Personalmente, encontré la configuración de la caché distribuida de eh cache muy sencilla.

El caché de EH analiza el problema con un poco más de detalle aquí: http://ehcache.org/documentation/ distribuido_caching.html

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top