PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Come aggiornare le entità JPA quando il database back-end cambia in modo asincrono?

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.