Domanda

Ho questo problema da molto tempo, ho cercato e chiuso il web e SO e non ho ancora trovato una soluzione. Spero che tu mi possa aiutare in questo.

Ho una relazione genitore-figlio tra due entità come la seguente:

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

    // ...
}

Il fatto è che quando creo un nuovo figlio e lo assegno a un genitore, il genitore non viene aggiornato quando è già nella cache.

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

 // ...

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

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

Ho provato a utilizzare @PreUpdate per aggiungere automaticamente il figlio al genitore quando il figlio è persistente, ma nel caso in cui abbiamo 2 gestori entità in 2 thread diversi (come in JBoss), il problema persiste ancora, fino a quando chiamiamo em.refresh(parent)

Quindi la domanda è: c'è un modo per eliminare senza problemi il problema e garantire che parent.getChildren () restituisca sempre l'elenco aggiornato dei bambini?

È stato utile?

Soluzione

La maggior parte degli ORM si comporterà in questo modo.

L'oggetto nella cache non viene aggiornato dal database (una lettura aggiuntiva che non è necessaria). Pensa anche al modello a oggetti e alla persistenza come separati. vale a dire mantenere il modello a oggetti coerente con se stesso e non fare affidamento sul meccanismo di persistenza per farlo per te.

Quindi, se desideri che l'oggetto venga aggiunto alla raccolta, fallo in " setParent " codice.

La migliore pratica in questo caso è in effetti quella di far sì che una parte della relazione faccia tutto il lavoro e lasci che l'altra parte vi rimandi. Inoltre suggerirei di utilizzare l'accesso al campo anziché l'accesso al metodo, in questo modo è possibile personalizzare i metodi con maggiore flessibilità.

Aggiungi un metodo al genitore chiamato addChild

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

e quindi imposta setParent in Child:

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

setParent0 in Child è la proprietà della proprietà per il genitore sul figlio.

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

Suggerirei anche che " getChildren " Il metodo restituisce una raccolta immutabile in modo che gli sviluppatori non utilizzino inavvertitamente questo metodo (ho imparato a fondo in tutto questo).

Un'altra cosa, dovresti avere un codice di controllo nullo e altri pezzi difensivi nel codice sopra, l'ho lasciato fuori per chiarezza.

Altri suggerimenti

Abbastanza sicuro che il tuo problema qui sia le impostazioni di Cascade.

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

    // ...
}

L'uso di queste impostazioni a cascata consentirà di mantenere in sequenza e gli aggiornamenti degli oggetti figlio.

ad es.

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

ulteriori informazioni al riguardo sono disponibili all'indirizzo Hibernate Annotations

Per quanto riguarda il problema con la memorizzazione nella cache, questo è un problema molto comune quando si hanno più macchine virtuali in esecuzione sullo stesso database con cache separate. Si chiama " cache drift " ;.

La maggior parte delle implementazioni della cache compatibili con l'ibernazione (ehcache, OSCache e SwarmCache) hanno una cache distribuita integrata che può essere utilizzata per sincronizzare le cache. La cache distribuita, in genere, invia messaggi multicast aggiornando lo stato della cache. Eseguendo uno sfratto di cache di secondo livello da parte di SessionFactory.evict (Class, id), ad esempio, verrà inviato un messaggio di invalidazione alle altre cache del cluster che invaliderà qualsiasi altra copia di quell'oggetto in altre cache.

A seconda della distribuzione, il multicast potrebbe essere o non essere accettabile per te. In caso contrario, potrebbe essere necessario utilizzare una soluzione a cache singola come memcached.

Ho trovato personalmente la configurazione della cache distribuita della cache di eh molto semplice.

La cache EH discute il problema in modo un po 'più dettagliato qui: http://ehcache.org/documentation/ distributed_caching.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top