2009-07-30 28 views
8

Tengo este problema desde hace mucho tiempo, he buscado en la web y SO dentro y fuera y no he encontrado una solución todavía. Espero que me puedas ayudar en eso.Hibernate @OneToMany con mappedBy (padre-hijo) relación y problema de caché

que tienen 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 se crea un nuevo hijo y asignarlo a un padre, el padre no se actualizan 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 

me han tratado de utilizar @PreUpdate para añadir automáticamente al niño a los padres cuando se conserva el niño, pero en el caso cuando tenemos 2 gestores de entidad en 2 hilos diferentes (como en JBoss), la cuestión todavía existe, hasta que llamemos al em.refresh(parent)

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

Respuesta

8

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

El objeto en la memoria 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 objetos coherente consigo mismo y no confíe en el mecanismo de persistencia para hacer esto por usted.

Así que si quiere que el objeto se agregue a la colección, hágalo en el código "setParent".

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

añadir un método para padres llamada addChild

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

y luego hacer setParent en Child:

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

setParent0 en niños es la stter propiedad de los padres de los niños.

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

Me gustaría también sugieren que el método "getChildren" devolver una colección inmutable para que los desarrolladores no sin darse cuenta, no utilizan este método (I aprendido de la manera difícil en todo esto).

Una cosa más, debe tener código de comprobación nulo y otras piezas defensivas en el código anterior, lo dejé fuera para mayor claridad.

+0

Gracias por su extensa respuesta, Michael. He encontrado algo de buena información en él. Pero, me temo, no resuelve el problema que tengo porque dos instancias diferentes de EntityManager tienen dos cachés diferentes y cuando uno de ellos actualiza una instancia de entidad, el otro no ve la actualización y su entidad en caché se vuelve obsoleta – artemb

+0

Suena como que necesita ver activadores de actualización que luego tomarán ese objeto y actualizarán el otro caché. O puede hacer que los dos miembros de caché pertenezcan al mismo clúster si almacena en clúster los soportes de solución de almacenamiento en memoria caché. –

+0

Lamentablemente no tengo control sobre la memoria caché de la sesión de Hibernate. ¿O yo? – artemb

1

En cuanto 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 la memoria caché".

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

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

Personalmente encontré la configuración del caché distribuido de eh cache muy simple.

caché EH discute el problema en detalle un poco más aquí: http://ehcache.org/documentation/distributed_caching.html

4

Bastante seguro de que su problema aquí es la configuración en 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; 

    // ... 
} 

Al usar estos ajustes de cascada persistirá la 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

Cuestiones relacionadas