Jag rekommenderar att du lägger till en @Startup
@Singleton
klass som upprättar en JDBC-anslutning till PostgreSQL-databasen och använder LISTEN
och NOTIFY
för att hantera cache-ogiltigförklaring.
Uppdatera :Här är ett annat intressant sätt att använda pgq och en samling arbetare för ogiltigförklaring.
Ogiltighetssignalering
Lägg till en trigger på tabellen som uppdateras som skickar en NOTIFY
närhelst en enhet uppdateras. På PostgreSQL 9.0 och över denna NOTIFY
kan innehålla en nyttolast, vanligtvis ett rad-ID, så att du inte behöver ogiltigförklara hela din cache, bara entiteten som har ändrats. På äldre versioner där en nyttolast inte stöds kan du antingen lägga till de ogiltiga posterna till en tidsstämplad loggtabell som din hjälparklass frågar när den får en NOTIFY
, eller bara ogiltigförklara hela cachen.
Din hjälpklass LISTEN
nu s på NOTIFY
händelser som utlösaren skickar. När den får en NOTIFY
händelse, kan den ogiltigförklara enskilda cache-poster (se nedan), eller tömma hela cachen. Du kan lyssna efter notiser från databasen med PgJDBC:s lyssna/notifiera support. Du måste ta bort alla anslutningspooler som hanteras java.sql.Connection
för att komma till den underliggande PostgreSQL-implementeringen så att du kan casta den till org.postgresql.PGConnection
och ring getNotifications()
på den.
Ett alternativ till LISTEN
och NOTIFY
, du kan polla en ändringsloggtabell på en timer och låta en trigger i problemtabellen lägga till ändrade rad-ID och ändra tidsstämplar till ändringsloggtabellen. Detta tillvägagångssätt kommer att vara portabelt förutom behovet av en annan utlösare för varje DB-typ, men det är ineffektivt och mindre aktuellt. Det kommer att kräva frekvent ineffektiv polling och fortfarande ha en tidsfördröjning som lyssnings-/notifieringsmetoden inte gör. I PostgreSQL kan du använda en UNLOGGED
tabell för att minska kostnaderna för detta tillvägagångssätt lite.
Cachenivåer
EclipseLink/JPA har ett par nivåer av caching.
Den första nivåns cache finns i EntityManager
nivå. Om en enhet är kopplad till en EntityManager
av persist(...)
, merge(...)
, find(...)
, etc, sedan EntityManager
krävs för att returnera samma instans av den entiteten när den nås igen inom samma session, oavsett om din applikation fortfarande har referenser till den eller inte. Den här bifogade instansen kommer inte att vara uppdaterad om ditt databasinnehåll sedan dess har ändrats.
Den andra nivåns cache, som är valfri, finns på EntityManagerFactory
nivå och är en mer traditionell cache. Det är inte klart om du har 2:a nivåns cache aktiverat. Kontrollera dina EclipseLink-loggar och din persistence.xml
. Du kan få tillgång till den andra nivåns cache med EntityManagerFactory.getCache()
; se Cache
.
@thedayofcondor visade hur man spolar 2:a nivåns cache med:
em.getEntityManagerFactory().getCache().evictAll();
men du kan också vräka enskilda objekt med evict(java.lang.Class cls, java.lang.Object primaryKey)
ring:
em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);
som du kan använda från din @Startup
@Singleton
NOTIFY
lyssnaren för att ogiltigförklara endast de poster som har ändrats.
Den första nivåns cache är inte så lätt, eftersom det är en del av din applikationslogik. Du kommer att vilja lära dig hur EntityManager
, bifogade och fristående enheter, etc. arbete. Ett alternativ är att alltid använda fristående entiteter för tabellen i fråga, där du använder en ny EntityManager
närhelst du hämtar enheten. Denna fråga:
Ogiltigförklarar JPA EntityManager-session
har en användbar diskussion om att hantera ogiltigförklaring av enhetshanterarens cache. Det är dock osannolikt att en EntityManager
cachen är ditt problem, eftersom en RESTful webbtjänst vanligtvis implementeras med kort EntityManager
sessioner. Detta är sannolikt bara ett problem om du använder utökade persistenskontexter eller om du skapar och hanterar din egen EntityManager
sessioner istället för att använda behållarhanterad persistens.