PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Como atualizar as entidades JPA quando o banco de dados de back-end é alterado de forma assíncrona?


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.