2011-09-15 8 views
9

Attualmente sto ruotando il mio piccolo ORM e mi trovo di fronte al compito di creare una mappatura canonica per evitare di caricare la stessa entità dal database più di una volta.Come implementare un mapping canonico in Java?

Il mio approccio attuale è quello di utilizzare un HashMap<Object, WeakReference<Object>>. La chiave è la chiave primaria dell'entità database mappata (uno ArrayList<Object> se è una chiave composta) ei valori sono WeakReference<Object>.

Il mio problema principale è come pulire la mappa? Quando un oggetto non viene più utilizzato, il riferimento debole nella mappa andrà a null e lo scoprirò solo alla prossima ricerca (o mai, se non guardo di nuovo l'oggetto). Potrei fare in modo che i riferimenti deboli si registrino con uno ReferenceQueue quando vengono cancellati, e quindi controllano la coda ogni volta che cerco qualcosa. Il riferimento cancellato non mi dava alcun suggerimento su quale oggetto fosse stato cancellato, quindi suppongo che avrei dovuto sottoclasse WeakReference per memorizzare la chiave nella mappa, così posso rimuoverla dopo che il riferimento è stato cancellato.

È questa la strada da percorrere o esiste un modo più semplice per implementarlo?

risposta

13

Si consiglia di utilizzare MapMaker di Guava o CacheBuilder in r10.

Consentono lo svuotamento automatico basato su tempo e dimensioni *, nonché il supporto di chiavi o valori deboli. (Le prossime CacheBuilder promesse debbano essere specificatamente su misura per questo tipo di caso d'uso.)

Quindi è possibile inizializzare la vostra mappa:

ConcurrentMap<Key, Object> cache = new MapMaker() 
     .weakValues() 
     .makeMap(); 

E il beneficio immediato sarà che quando un valore non è garbage collection, il l'intera voce sarà rimossa. Inoltre, è possibile utilizzare una mappa di calcolo:

ConcurrentMap<Key, Object> cache = new MapMaker() 
     .weakValues() 
     .makeComputingMap(loadFunction); 

dove loadFunction è un Function<Key, Object> che carica un oggetto dal database. Il vantaggio di questo è che la mappa gestirà richieste concorrenti per un particolare oggetto, assicurando che la query venga richiamata una sola volta. Inoltre, il codice richiedente deve solo chiamare get() e può sempre aspettarsi l'oggetto, sia dalla cache che dal database.

Questi esempi utilizzano MapMaker - Non ho ancora avuto il piacere di giocare con CacheBuilder.

Vedere la mia domanda my ideal cache using guava per ulteriori esempi. Questo post discute su come combinare lo sfratto basato sul tempo con la canonicalizzazione.

+2

All'inizio ho esitato a aggiungere questa libreria, ma ora che l'ho fatto, mi chiedo perché non l'ho fatto prima. Grazie per questa grande risposta! –