Eu recomendo adicionar um
@Startup
@Singleton
classe que estabelece uma conexão JDBC com o banco de dados PostgreSQL e usa LISTEN
e NOTIFY
para lidar com a invalidação de cache. Atualizar :Aqui está outra abordagem interessante, usando pgq e uma coleção de trabalhadores para invalidação.
Sinalização de invalidação
Adicione um gatilho na tabela que está sendo atualizada que envia um
NOTIFY
sempre que uma entidade é atualizada. No PostgreSQL 9.0 e acima, este NOTIFY
pode conter uma carga útil, geralmente um ID de linha, para que você não precise invalidar todo o cache, apenas a entidade que foi alterada. Em versões mais antigas em que uma carga útil não é suportada, você pode adicionar as entradas invalidadas a uma tabela de log com carimbo de data/hora que sua classe auxiliar consulta quando recebe um NOTIFY
, ou apenas invalidar todo o cache. Sua classe auxiliar agora
LISTEN
s no NOTIFY
eventos que o gatilho envia. Quando recebe um NOTIFY
evento, ele pode invalidar entradas de cache individuais (veja abaixo) ou liberar todo o cache. Você pode escutar notificações do banco de dados com o suporte escutar/notificar do PgJDBC. Você precisará desempacotar qualquer pool de conexão gerenciado java.sql.Connection
para obter a implementação subjacente do PostgreSQL para que você possa convertê-la em org.postgresql.PGConnection
e chame getNotifications()
nele. Uma alternativa para
LISTEN
e NOTIFY
, você pode pesquisar uma tabela de log de alterações em um cronômetro e ter um gatilho na tabela de problemas anexando IDs de linha alterados e carimbos de data/hora de alteração à tabela de log de alterações. Essa abordagem será portátil, exceto pela necessidade de um gatilho diferente para cada tipo de banco de dados, mas é ineficiente e menos oportuna. Isso exigirá pesquisas ineficientes frequentes e ainda terá um atraso de tempo que a abordagem de escuta/notificação não possui. No PostgreSQL você pode usar um UNLOGGED
tabela para reduzir um pouco os custos dessa abordagem. Níveis de cache
EclipseLink/JPA tem alguns níveis de cache.
O cache de 1º nível está no
EntityManager
nível. Se uma entidade estiver anexada a um EntityManager
por persist(...)
, merge(...)
, find(...)
, etc, então o EntityManager
é necessário retornar a mesma instância dessa entidade quando ele é acessado novamente na mesma sessão, independentemente de seu aplicativo ainda ter referências a ele. Esta instância anexada não será atualizada se o conteúdo do banco de dados tiver sido alterado. O cache de 2º nível, que é opcional, está no
EntityManagerFactory
level e é um cache mais tradicional. Não está claro se você tem o cache de 2º nível ativado. Verifique seus logs do EclipseLink e seu persistence.xml
. Você pode obter acesso ao cache de 2º nível com EntityManagerFactory.getCache()
; veja Cache
. @thedayofcondor mostrou como liberar o cache de 2º nível com:
em.getEntityManagerFactory().getCache().evictAll();
mas você também pode despejar objetos individuais com o
evict(java.lang.Class cls, java.lang.Object primaryKey)
ligar:em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);
que você pode usar em seu
@Startup
@Singleton
NOTIFY
listener para invalidar apenas as entradas que foram alteradas. O cache de 1º nível não é tão fácil, pois faz parte da lógica da sua aplicação. Você vai querer saber como o
EntityManager
, entidades anexadas e destacadas, etc funcionam. Uma opção é sempre usar entidades desanexadas para a tabela em questão, onde você usa um novo EntityManager
sempre que você buscar a entidade. Essa questão:Invalidando a sessão do JPA EntityManager
tem uma discussão útil sobre como lidar com a invalidação do cache do gerenciador de entidade. No entanto, é improvável que um
EntityManager
cache é o seu problema, porque um serviço da Web RESTful geralmente é implementado usando o curto EntityManager
sessões. É provável que isso seja um problema apenas se você estiver usando contextos de persistência estendidos ou se estiver criando e gerenciando seu próprio EntityManager
sessões em vez de usar persistência gerenciada por contêiner.