Recomiendo agregar un @Startup
@Singleton
clase que establece una conexión JDBC a la base de datos PostgreSQL y usa LISTEN
y NOTIFY
para manejar la invalidación de caché.
Actualizar :Aquí hay otro enfoque interesante, usando pgq y una colección de trabajadores para la invalidación.
Señalización de invalidación
Agrega un disparador en la tabla que se está actualizando que envía un NOTIFY
cada vez que se actualiza una entidad. En PostgreSQL 9.0 y superior este NOTIFY
puede contener una carga útil, generalmente una ID de fila, por lo que no tiene que invalidar todo su caché, solo la entidad que ha cambiado. En versiones anteriores en las que no se admite una carga útil, puede agregar las entradas invalidadas a una tabla de registro con marca de tiempo que su clase auxiliar consulta cuando recibe un NOTIFY
, o simplemente invalidar todo el caché.
Tu clase auxiliar ahora LISTEN
s en el NOTIFY
eventos que envía el activador. Cuando recibe un NOTIFY
evento, puede invalidar entradas de caché individuales (ver a continuación) o vaciar todo el caché. Puede escuchar las notificaciones de la base de datos con el soporte de escucha/notificación de PgJDBC. Deberá desenvolver cualquier conexión java.sql.Connection
administrada por el agrupador de conexiones para llegar a la implementación subyacente de PostgreSQL para que pueda enviarla a org.postgresql.PGConnection
y llama a getNotifications()
en él.
Una alternativa a LISTEN
y NOTIFY
, puede sondear una tabla de registro de cambios en un temporizador y hacer que un disparador en la tabla de problemas agregue los ID de fila cambiados y las marcas de tiempo de cambio a la tabla de registro de cambios. Este enfoque será portátil excepto por la necesidad de un activador diferente para cada tipo de base de datos, pero es ineficiente y menos oportuno. Requerirá un sondeo frecuente e ineficiente, y aún tendrá un retraso de tiempo que no tiene el enfoque de escucha/notificación. En PostgreSQL puede usar un UNLOGGED
para reducir un poco los costos de este enfoque.
Niveles de caché
EclipseLink/JPA tiene un par de niveles de almacenamiento en caché.
El caché de primer nivel está en el EntityManager
nivel. Si una entidad está adjunta a un EntityManager
por persist(...)
, merge(...)
, find(...)
, etc., entonces el EntityManager
se requiere para devolver la misma instancia de esa entidad cuando se accede de nuevo dentro de la misma sesión, ya sea que su aplicación todavía tenga referencias a ella o no. Esta instancia adjunta no estará actualizada si el contenido de su base de datos ha cambiado desde entonces.
El caché de segundo nivel, que es opcional, está en EntityManagerFactory
nivel y es un caché más tradicional. No está claro si tiene habilitado el caché de segundo nivel. Verifique sus registros de EclipseLink y su persistence.xml
. Puede obtener acceso al caché de segundo nivel con EntityManagerFactory.getCache()
; ver Cache
.
@thedayofcondor mostró cómo vaciar el caché de segundo nivel con:
em.getEntityManagerFactory().getCache().evictAll();
pero también puede desalojar objetos individuales con evict(java.lang.Class cls, java.lang.Object primaryKey)
llamar:
em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);
que puedes usar desde tu @Startup
@Singleton
NOTIFY
oyente para invalidar solo aquellas entradas que han cambiado.
El caché de primer nivel no es tan fácil, porque es parte de la lógica de su aplicación. Querrá saber cómo el EntityManager
, entidades adscritas y separadas, etc. Una opción es usar siempre entidades separadas para la tabla en cuestión, donde usa un nuevo EntityManager
cada vez que buscas la entidad. Esta pregunta:
Invalidando la sesión de JPA EntityManager
tiene una discusión útil sobre el manejo de la invalidación del caché del administrador de la entidad. Sin embargo, es poco probable que un EntityManager
el caché es su problema, porque un servicio web RESTful generalmente se implementa usando EntityManager
corto sesiones Es probable que esto solo sea un problema si está utilizando contextos de persistencia extendida, o si está creando y administrando su propio EntityManager
sesiones en lugar de utilizar la persistencia administrada por contenedor.