Ich empfehle das Hinzufügen eines @Startup
@Singleton
Klasse, die eine JDBC-Verbindung zur PostgreSQL-Datenbank herstellt und LISTEN
verwendet und NOTIFY
um die Cache-Invalidierung zu handhaben.
Aktualisieren :Hier ist ein weiterer interessanter Ansatz, der pgq und eine Sammlung von Arbeitern für die Invalidierung verwendet.
Invalidierungssignalisierung
Fügen Sie der zu aktualisierenden Tabelle einen Trigger hinzu, der ein NOTIFY
sendet immer wenn eine Entität aktualisiert wird. Auf PostgreSQL 9.0 und höher dieses NOTIFY
kann eine Nutzlast enthalten, normalerweise eine Zeilen-ID, sodass Sie nicht Ihren gesamten Cache ungültig machen müssen, sondern nur die Entität, die sich geändert hat. In älteren Versionen, in denen eine Payload nicht unterstützt wird, können Sie entweder die ungültigen Einträge zu einer mit Zeitstempel versehenen Protokolltabelle hinzufügen, die Ihre Hilfsklasse abfragt, wenn sie ein NOTIFY
erhält , oder einfach den gesamten Cache ungültig machen.
Ihre Hilfsklasse ist jetzt LISTEN
s auf NOTIFY
Ereignisse, die der Trigger sendet. Wenn es ein NOTIFY
erhält Event kann es einzelne Cache-Einträge ungültig machen (siehe unten) oder den gesamten Cache leeren. Mit der Listen/Notify-Unterstützung von PgJDBC können Sie auf Benachrichtigungen aus der Datenbank warten. Sie müssen alle vom Verbindungspooler verwalteten java.sql.Connection
entpacken um zur zugrunde liegenden PostgreSQL-Implementierung zu gelangen, damit Sie sie in org.postgresql.PGConnection
umwandeln können und rufen Sie getNotifications()
auf drauf.
Eine Alternative zu LISTEN
und NOTIFY
, könnten Sie eine Änderungsprotokolltabelle mit einem Timer abfragen und einen Trigger für die Problemtabelle haben, der geänderte Zeilen-IDs anhängt und Zeitstempel an die Änderungsprotokolltabelle ändert. Dieser Ansatz ist übertragbar, abgesehen von der Notwendigkeit eines anderen Triggers für jeden DB-Typ, aber er ist ineffizient und weniger zeitgemäß. Es erfordert ein häufiges ineffizientes Abfragen und hat dennoch eine Zeitverzögerung, die der Listen/Notify-Ansatz nicht hat. In PostgreSQL können Sie einen UNLOGGED
verwenden um die Kosten dieses Ansatzes etwas zu reduzieren.
Cache-Ebenen
EclipseLink/JPA hat mehrere Caching-Ebenen.
Der Cache der 1. Ebene befindet sich im EntityManager
Stufe. Wenn eine Entität an einen EntityManager
angehängt ist durch persist(...)
, merge(...)
, find(...)
, etc, dann den EntityManager
ist erforderlich, um dieselbe Instanz dieser Entität zurückzugeben bei einem erneuten Zugriff innerhalb derselben Sitzung, unabhängig davon, ob Ihre Anwendung noch darauf verweist. Diese angehängte Instanz ist nicht mehr aktuell, wenn sich Ihre Datenbankinhalte seitdem geändert haben.
Der 2nd-Level-Cache, der optional ist, befindet sich in der EntityManagerFactory
Level und ist ein eher traditioneller Cache. Es ist nicht klar, ob Sie den 2nd-Level-Cache aktiviert haben. Überprüfen Sie Ihre EclipseLink-Protokolle und Ihre persistence.xml
. Mit EntityManagerFactory.getCache()
erhalten Sie Zugriff auf den 2nd-Level-Cache; siehe Cache
.
@thedayofcondor zeigte, wie man den 2nd-Level-Cache leert mit:
em.getEntityManagerFactory().getCache().evictAll();
Sie können aber auch einzelne Objekte mit evict(java.lang.Class cls, java.lang.Object primaryKey)
entfernen Aufruf:
em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);
die Sie von Ihrem @Startup
aus verwenden können @Singleton
NOTIFY
Listener, um nur die Einträge ungültig zu machen, die sich geändert haben.
Der 1st-Level-Cache ist nicht so einfach, da er Teil Ihrer Anwendungslogik ist. Sie sollten erfahren, wie der EntityManager
, angeschlossene und freistehende Einheiten usw. arbeiten. Eine Möglichkeit besteht darin, immer getrennte Entitäten für die betreffende Tabelle zu verwenden, wobei Sie einen neuen EntityManager
verwenden wann immer Sie die Entität abrufen. Diese Frage:
JPA EntityManager-Sitzung wird ungültig
hat eine nützliche Diskussion über den Umgang mit der Invalidierung des Caches des Entitätsmanagers. Es ist jedoch unwahrscheinlich, dass ein EntityManager
Cache ist Ihr Problem, denn ein RESTful Webservice wird normalerweise mit dem kurzen EntityManager
implementiert Sitzungen. Dies ist wahrscheinlich nur dann ein Problem, wenn Sie erweiterte Persistenzkontexte verwenden oder wenn Sie Ihren eigenen EntityManager
erstellen und verwalten Sitzungen, anstatt Container-verwaltete Persistenz zu verwenden.