2009-07-30 6 views
8

Ich habe dieses Problem für eine lange Zeit jetzt, ich habe das Internet und SO hinein und heraus gesucht und habe noch keine Lösung gefunden. Ich hoffe du kannst mir dabei helfen.Hibernate @ OneToMany mit mappedBy (Eltern-Kind) Beziehung und Cache-Problem

Ich habe eine Eltern-Kind-Beziehung zwischen zwei Entitäten wie folgt aus:

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

    // ... 
} 

Die Sache ist, dass, wenn ich ein neues Kind schaffen und es zu einem übergeordneten zuweisen, die Mutter nicht aktualisiert, wenn Es ist bereits im Cache.

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

// ... 

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

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

Ich habe versucht, @PreUpdate zu verwenden, um automatisch das Kind zum Vater hinzu, wenn das Kind beibehalten wird, aber in dem Fall, wenn haben wir 2 Entity-Manager in zwei verschiedenen Threads (wie in JBoss), das Thema existiert noch, bis wir anrufen em.refresh(parent)

So ist die Frage - gibt es einen Weg, um das Problem reibungslos zu beseitigen und sicherzustellen, dass parent.getChildren() immer die aktuelle Liste der Kinder zurückgeben?

Antwort

8

Die meisten ORMs werden sich so verhalten.

Das Objekt im Cache wird nicht aus der Datenbank aktualisiert (ein zusätzlicher Lesevorgang ist nicht erforderlich). Denken Sie auch an das Objektmodell und die Persistenz als getrennt. Halten Sie Ihr Objektmodell mit sich selbst konsistent und verlassen Sie sich nicht auf den Persistenzmechanismus, um dies für Sie zu tun.

Wenn Sie möchten, dass das Objekt zur Sammlung hinzugefügt wird, dann tun Sie das im Code "setParent".

Die beste Praxis in diesem Fall ist in der Tat, um eine Seite der Beziehung die ganze Arbeit machen zu lassen und die andere Seite auf sich aufschieben zu lassen. Außerdem würde ich vorschlagen, Feldzugriff statt Methodenzugriff zu verwenden, damit Sie Methoden mit größerer Flexibilität anpassen können.

ein Verfahren zur Mutter hinzufügen addChild genannt

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

und dann setParent in Kindern machen:

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

setParent0 in Kind ist Eigentum stter für Eltern auf Kind.

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

Ich würde auch vorschlagen, dass die „getChildren“ Methode eine unveränderliche Sammlung zurückgeben, so dass Entwickler nicht versehentlich diese Methode nicht verwenden (ich auf die harte Art und Weise in all diesem gelernt).

Noch eine Sache, sollten Sie Null Prüfcode und andere defensive Stücke in den obigen Code haben, ich habe es aus Gründen der Klarheit weggelassen.

+0

Vielen Dank für Ihre umfassende Antwort, Michael zu finden. Ich habe ein paar gute Informationen gefunden. Aber ich fürchte, es löst nicht das Problem, das ich habe, weil zwei verschiedene EntityManager-Instanzen zwei verschiedene Caches enthalten und wenn einer von ihnen eine Entitätsinstanz aktualisiert, sieht der andere die Aktualisierung nicht und die zwischengespeicherte Entität wird veraltet – artemb

+0

Klingt so, als müssten Sie sich die Update-Trigger ansehen, die dann das Objekt übernehmen und den anderen Cache aktualisieren. Oder Sie können diese beiden Caches zu Mitgliedern desselben Clusters machen, wenn Sie das Clustern von Lösungs-Clustern zwischenspeichern. –

+0

Leider habe ich keinen Einfluss auf den Sitzungscache von Hibernate. Oder habe ich? – artemb

1

In Bezug auf das Problem beim Caching ist dies ein sehr häufiges Problem, wenn mehrere VMs mit derselben Datenbank mit separaten Caches ausgeführt werden. Es heißt "Cache Drift".

Die meisten hibernatefreundlichen Cache-Implementierungen (ehcache, OSCache und SwarmCache) verfügen über einen integrierten integrierten Cache, der zum Synchronisieren der Caches verwendet werden kann. Der verteilte Cache sendet im Allgemeinen Multicast-Nachrichten, die den Zustand des Cache aktualisieren. Eine Cache-Räumung auf der zweiten Ebene beispielsweise durch SessionFactory.evict (Class, ID) bewirkt, dass eine Ungültigkeitsnachricht an die anderen Caches im Cluster gesendet wird, wodurch alle anderen Kopien dieses Objekts in anderen Caches ungültig werden.

Je nach Ihrer Bereitstellung ist das Multicast möglicherweise für Sie akzeptabel oder nicht. Wenn dies nicht der Fall ist, müssen Sie möglicherweise eine Single-Cache-Lösung wie Memcached verwenden.

Ich persönlich fand die Konfiguration des verteilten Cache-Cache sehr einfach.

EH Cache diskutiert das Problem in etwas mehr Detail hier: http://ehcache.org/documentation/distributed_caching.html

4

Ziemlich sicher, dass Ihr Problem hier ist Ihre Cascade Einstellungen.

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

    // ... 
} 

Mit diesen Kaskadeneinstellungen wird die Kaskadierung beibehalten und auf untergeordnete Objekte aktualisiert.

z.

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 

oder

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

child.getParent(); // returns the parent 

weitere Informationen dazu finden Sie unter Hibernate Annotations