7

C'è un modo per utilizzare gli oggetti Flyweight con la mappatura di persistenza in letargo? Il mio modello di dati contiene molti oggetti che saranno uguali. Invece di avere un'istanza separata per ciascuno di quegli stessi oggetti, mi piacerebbe utilizzare il modello Flyweight Design e fare sempre riferimento allo stesso oggetto fisico. Come ottenere questo in ibernazione?Hibernate and Flyweight

Btw. tutte le JVM ottimizzano l'uso delle stringhe in modo tale che quando la stessa stringa viene utilizzata più volte, sarà sempre la stessa istanza fisica?

risposta

3

Dipende.

Per i valori in lettura valori è possibile implementare facilmente un modello di peso mosca creando un tipo di utente personalizzato che restituirà oggetti da un pool anziché da nuove istanze ogni volta.

Per le entità Hibernate è di default sensato e vuole essere coerente tra le transazioni e quindi non condividerà le entità tra le sessioni per evitare le condizioni di gara dei dati - e non penso che sia quello che vuoi.

Ma nel caso in cui sia (e questo è totalmente non raccomandato senza realmente sapere cosa si sta facendo) è possibile implementare Interceptor.getEntity() che è inteso per il caching di secondo livello. In questo metodo puoi restituire un'entità (anche se condivisa da altre sessioni) e avrai effettivamente un modello di peso mosca per le tue entità.

MA Mi raccomando fortemente contro questo per la consistenza dei dati - molto meglio avere valori di peso reale immutabili referenziati da entità che anche provare e volare pesi reali entità.

+0

Il tipo di utente è una buona idea per mappare lo stato comune delle istanze Flyweight. (Hibernate e JDBC inizieranno ancora un sacco di istanze. Non c'è modo di aggirarlo. Almeno saranno garbage collection abbastanza rapidamente.) Un CompositeUserType può rappresentare l'intero stato comune in un oggetto? –

+0

Sì, un tipo di utente composito potrebbe probabilmente farlo, ma allora perché avere l'entità come entità in primo luogo? Basta che sia un oggetto valore (componente) dall'inizio. –

0

Se gli oggetti implementano l'uguaglianza per identità, Hibernate avrà solo la singola istanza associata a quella chiave primaria. Non credo che sia esattamente la stessa idea di Flyweight, ma il punto è che non avrai molte istanze dello stesso oggetto Hibernate.

2

Sì, è possibile implementare il modello Flyweight con Hibernate.

Il modello di peso mosca consente di ridurre al minimo l'utilizzo della memoria per istanza. La strategia è quella di condividere quanto più stato possibile tra le istanze di Flyweight. Nel tuo caso lo stato condivisibile è , eccetto l'identificatore di oggetto di ibernazione e qualche stato aggiuntivo per mantenere l'identità dell'oggetto.

Ciascuna istanza di tipo flyweight ha bisogno del proprio object identity. Lo stato aggiuntivo è il modo di implementare l'identità per distinguere tra oggetti che condividono lo stato comune.

public boolean equals(Object obj){ 
    Fly other; [..]//check type 
    //null checks ommitted 
    return other.myState.equals(myState) && other.commonState.equals(commonState); 
} 

Se l'identità di un oggetto è condiviso tra le istanze di sospensione sarebbe interpretare tutte le istanze fisiche (riferimenti) come la stessa istanza. Hibernate utilizza il metodo equals per verificare l'identità dell'oggetto e l'implementazione equa dovrebbe restituire (! a.equals(a) == true) che è illegale. Equal deve essere riflessivo. Se si rompe questo contratto, tutte le librerie che dipendono dal contratto verranno interrotte (raccolte, ibernazione, ecc.).

Non è possibile implementare il metodo di uguale utilizzando l'identificatore di oggetto di ibernazione per distinguere tra gli oggetti. Ciò renderebbe l'identità dell'oggetto dipendente dallo stato di persistenza (persistente o transitorio).

Un modo per modellare lo stato comune in modalità di sospensione è un'associazione uno-a-molti tra oggetti di stato condivisi e oggetti volanti. (Forse qualcuno ha un'idea di come mappare i dati senza unire due tabelle?)

Stringa: Solo internalized strings sarà condiviso. Questa non è la soluzione migliore per la maggior parte del tempo. È appropriato per i simboli (nome classe, nome metodo, ecc.). Le stringhe internalizzate non verranno mai raccolte da garbage collection e sarà necessario disporre di un'istanza String che verrà raccolta automaticamente in ogni caso new String("..").intern(). Non salverà le allocazioni. C'è solo il piccolo vantaggio che la stringa di base non sopravviverà alla generazione gc o potrebbe essere allocata nello stack (con l'analisi di escape in hot spot abilitata e applicabile).

1

tutte le JVM ottimizzano l'utilizzo di stringhe in modo tale che quando la stessa stringa viene utilizzata più volte, sarà sempre la stessa istanza fisica?

Ne dubito molto.Nello stesso file di classe, quando definito come:

String s1 = "Yes"; 
String s2 = "Yes"; 

probabilmente avrai s1 == s1.

Ma se avete come:

String x = loadTextFromFile(); // file contains es 
StringBuilder b = new StringBuilder(); 
s2 = b.append("Y").append(x).toString(); // s2 = "Yes" 

Non credo che il runtime è andare a verificare e confrontare tutte le stringhe caricati per il valore di ritorno di loro costruttore.

Quindi, confrontare sempre gli oggetti con uguale(). Questo è un buon consiglio in ogni caso dal momento che ogni equivale equivale a:

if (this == o) { 
    return true; 
} 
+0

Non si tratta di confronto, ma piuttosto di consumo di memoria. Come menziona Thomas Jung, puoi usare il metodo String.intern() per ottenere la stringa sottostante. Provalo! – paweloque

+0

Quello che volevo dire è che se un valore di stringa non è noto al momento della compilazione, o non può essere precalcolato, l'oggetto sarà diverso (no ==) anche se ha, per coincidenza, lo stesso valore. – extraneon