Consiglio di aggiungere un @Startup
@Singleton
classe che stabilisce una connessione JDBC al database PostgreSQL e utilizza LISTEN
e NOTIFY
per gestire l'invalidazione della cache.
Aggiorna :ecco un altro approccio interessante, utilizzando pgq e una raccolta di lavoratori per l'invalidazione.
Segnalazione di invalidamento
Aggiungi un trigger sulla tabella in fase di aggiornamento che invii un NOTIFY
ogni volta che un'entità viene aggiornata. Su PostgreSQL 9.0 e versioni successive questo NOTIFY
può contenere un payload, di solito un ID riga, quindi non devi invalidare l'intera cache, solo l'entità che è cambiata. Nelle versioni precedenti in cui un payload non è supportato puoi aggiungere le voci invalidate a una tabella di registro con timestamp che la tua classe helper interroga quando riceve un NOTIFY
o semplicemente invalidare l'intera cache.
La tua classe di supporto ora LISTEN
s sul NOTIFY
eventi inviati dal trigger. Quando riceve un NOTIFY
evento, può invalidare singole voci della cache (vedi sotto) o svuotare l'intera cache. È possibile ascoltare le notifiche dal database con il supporto di ascolto/notifica di PgJDBC. Sarà necessario annullare il wrapping di tutti i pool di connessioni gestiti java.sql.Connection
per arrivare all'implementazione PostgreSQL sottostante in modo da poterla trasmettere a org.postgresql.PGConnection
e chiama getNotifications()
su di esso.
Un'alternativa a LISTEN
e NOTIFY
, è possibile eseguire il polling di una tabella del registro delle modifiche su un timer e fare in modo che un trigger nella tabella dei problemi aggiunga gli ID riga modificati e modifichino i timestamp alla tabella del registro delle modifiche. Questo approccio sarà portatile, fatta eccezione per la necessità di un trigger diverso per ogni tipo di DB, ma è inefficiente e meno tempestivo. Richiederà frequenti sondaggi inefficienti e avrà comunque un ritardo rispetto all'approccio di ascolto/notifica. In PostgreSQL puoi usare un UNLOGGED
tabella per ridurre un po' i costi di questo approccio.
Livelli della cache
EclipseLink/JPA ha un paio di livelli di memorizzazione nella cache.
La cache di 1° livello si trova in EntityManager
livello. Se un'entità è collegata a un EntityManager
di persist(...)
, merge(...)
, find(...)
, ecc, quindi EntityManager
è necessario restituire la stessa istanza di tale entità quando si accede nuovamente all'interno della stessa sessione, indipendentemente dal fatto che l'applicazione abbia ancora riferimenti ad essa o meno. Questa istanza allegata non sarà aggiornata se il contenuto del database è stato modificato da allora.
La cache di 2° livello, che è facoltativa, si trova in EntityManagerFactory
livello ed è una cache più tradizionale. Non è chiaro se hai abilitato la cache di 2° livello. Controlla i log di EclipseLink e il tuo persistence.xml
. Puoi accedere alla cache di 2° livello con EntityManagerFactory.getCache()
; vedi Cache
.
@thedayofcondor ha mostrato come svuotare la cache di 2° livello con:
em.getEntityManagerFactory().getCache().evictAll();
ma puoi anche eliminare singoli oggetti con evict(java.lang.Class cls, java.lang.Object primaryKey)
chiama:
em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);
che puoi utilizzare dal tuo @Startup
@Singleton
NOTIFY
listener per invalidare solo le voci che sono state modificate.
La cache di 1° livello non è così semplice, perché fa parte della logica dell'applicazione. Ti consigliamo di scoprire come funziona EntityManager
, entità attaccate e distaccate, ecc. Un'opzione consiste nell'utilizzare sempre entità separate per la tabella in questione, in cui si utilizza un nuovo EntityManager
ogni volta che recuperi l'entità. Questa domanda:
Invalidazione della sessione di JPA EntityManager
ha un'utile discussione sulla gestione dell'invalidazione della cache del gestore entità. Tuttavia, è improbabile che un EntityManager
la cache è il tuo problema, perché un servizio web RESTful viene solitamente implementato utilizzando EntityManager
breve sessioni. È probabile che questo sia un problema solo se stai utilizzando contesti di persistenza estesi o se stai creando e gestendo il tuo EntityManager
sessioni anziché utilizzare la persistenza gestita dal contenitore.