Jeg anbefaler at tilføje en @Startup
@Singleton
klasse, der etablerer en JDBC-forbindelse til PostgreSQL-databasen og bruger LISTEN
og NOTIFY
for at håndtere cache-invalidering.
Opdater :Her er en anden interessant tilgang, ved at bruge pgq og en samling arbejdere til ugyldiggørelse.
Ugyldighedssignalering
Tilføj en trigger på tabellen, der opdateres, og som sender en NOTIFY
hver gang en enhed opdateres. På PostgreSQL 9.0 og over denne NOTIFY
kan indeholde en nyttelast, normalt et række-id, så du ikke behøver at ugyldiggøre hele din cache, kun den enhed, der er ændret. På ældre versioner, hvor en nyttelast ikke understøttes, kan du enten tilføje de ugyldige poster til en tidsstemplet logtabel, som din hjælperklasse forespørger, når den får en NOTIFY
, eller bare ugyldiggøre hele cachen.
Din hjælperklasse nu LISTEN
s på NOTIFY
hændelser, som triggeren sender. Når den får en NOTIFY
hændelse, kan den ugyldiggøre individuelle cacheposter (se nedenfor) eller tømme hele cachen. Du kan lytte efter notifikationer fra databasen med PgJDBC's lytte/notify support. Du bliver nødt til at udpakke enhver forbindelsespooler-administreret java.sql.Connection
for at komme til den underliggende PostgreSQL-implementering, så du kan caste den til org.postgresql.PGConnection
og kald getNotifications()
på den.
Et alternativ til LISTEN
og NOTIFY
, kan du polle en ændringslogtabel på en timer og få en trigger på problemtabellen til at tilføje ændrede række-id'er og ændre tidsstempler til ændringslogtabellen. Denne tilgang vil være bærbar, bortset fra behovet for en anden trigger for hver DB-type, men den er ineffektiv og mindre rettidig. Det vil kræve hyppige ineffektive afstemninger og stadig have en tidsforsinkelse, som lyt/besked-tilgangen ikke gør. I PostgreSQL kan du bruge en UNLOGGED
tabel for at reducere omkostningerne ved denne fremgangsmåde en lille smule.
Cacheniveauer
EclipseLink/JPA har et par niveauer af caching.
Cachen på 1. niveau er i EntityManager
niveau. Hvis en enhed er knyttet til en EntityManager
af persist(...)
, merge(...)
, find(...)
osv., derefter EntityManager
er forpligtet til at returnere den samme forekomst af den pågældende enhed når den åbnes igen inden for samme session, uanset om din applikation stadig har referencer til den. Denne vedhæftede instans vil ikke være opdateret, hvis dit databaseindhold siden er ændret.
Cachen på 2. niveau, som er valgfri, er på EntityManagerFactory
niveau og er en mere traditionel cache. Det er ikke klart, om du har 2. niveaus cache aktiveret. Tjek dine EclipseLink-logfiler og din persistence.xml
. Du kan få adgang til cachen på 2. niveau med EntityManagerFactory.getCache()
; se Cache
.
@thedayofcondor viste, hvordan man skyller 2. niveaus cache med:
em.getEntityManagerFactory().getCache().evictAll();
men du kan også smide individuelle objekter ud med evict(java.lang.Class cls, java.lang.Object primaryKey)
ring:
em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);
som du kan bruge fra din @Startup
@Singleton
NOTIFY
lytteren for kun at ugyldiggøre de poster, der er ændret.
Cachen på 1. niveau er ikke så let, fordi den er en del af din applikationslogik. Du vil gerne lære om, hvordan EntityManager
, tilknyttede og fritliggende enheder osv. arbejde. En mulighed er altid at bruge adskilte entiteter til den pågældende tabel, hvor du bruger en ny EntityManager
hver gang du henter enheden. Dette spørgsmål:
Ugyldiggør JPA EntityManager-session
har en nyttig diskussion om håndtering af ugyldiggørelse af enhedsadministratorens cache. Det er dog usandsynligt, at en EntityManager
cachen er dit problem, fordi en RESTful webservice normalt implementeres ved hjælp af kort EntityManager
sessioner. Dette er sandsynligvis kun et problem, hvis du bruger udvidede persistenskontekster, eller hvis du opretter og administrerer din egen EntityManager
sessioner i stedet for at bruge containerstyret persistens.