后端数据库异步更改时如何刷新JPA实体?
我有一个带有某些表和视图的PostgreSQL 8.4数据库,这些表和视图本质上是对某些表的联接。我使用的NetBeans
7.2(如描述这里)来从这些观点和表导出并部署那些到Glassfish的3.1.2.2服务器基于REST的服务。
还有另一个过程可以异步更新用于构建视图的某些表中的内容。我可以直接查询视图和表,并查看这些更改已正确发生。但是,从基于REST的服务中提取时,这些值与数据库中的值不同。我认为这是因为JPA已在Glassfish服务器上缓存了数据库内容的本地副本,并且JPA需要刷新关联的实体。
我尝试将一些方法添加到NetBeans生成的AbstractFacade类中:
public abstract class AbstractFacade<T> { private Class<T> entityClass;
private String entityName;
private static boolean _refresh = true;
public static void refresh() { _refresh = true; }
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
this.entityName = entityClass.getSimpleName();
}
private void doRefresh() {
if (_refresh) {
EntityManager em = getEntityManager();
em.flush();
for (EntityType<?> entity : em.getMetamodel().getEntities()) {
if (entity.getName().contains(entityName)) {
try {
em.refresh(entity);
// log success
}
catch (IllegalArgumentException e) {
// log failure ... typically complains entity is not managed
}
}
}
_refresh = false;
}
}
...
}
然后doRefresh()
,我从find
NetBeans生成的每个方法中调用。什么通常发生是IllegalArgumentsException
像被抛出,说明somethngCan
not refresh not managed object: EntityTypeImpl@28524907:MyView [ javaType:
class org.my.rest.MyView descriptor: RelationalDescriptor(org.my.rest.MyView
--> [DatabaseTable(my_view)]), mappings: 12].
因此,我正在寻找有关如何正确刷新与视图关联的实体的最新建议。
事实证明我对基本问题的理解不正确。这在某种程度上与我先前发布的另一个问题有关,即视图没有单个字段可以用作唯一标识符。NetBeans要求我选择一个ID字段,因此我只选择了应该是多部分密钥的一部分。这表现出以下行为:所有具有特定ID字段的记录都是相同的,即使数据库具有具有相同ID字段的记录,但其余部分也不相同。JPA只看了我告诉它的是唯一标识符,并简单地提取了它找到的第一条记录。
我通过添加一个唯一的标识符字段解决了这一问题(从未使多部分密钥能够正常工作)。
回答:
我建议添加一个@Startup
@Singleton
类,该类建立与PostgreSQL数据库的JDBC连接,并使用LISTEN
和NOTIFY
处理高速缓存失效。
:这是另一种有趣的方法,使用pgq和一组worker进行失效。
回答:
在要更新的表上添加一个触发器,该触发器在NOTIFY
实体更新时发送一个。在PostgreSQL
9.0及更高版本上,它NOTIFY
可以包含有效负载,通常是行ID,因此您不必使整个缓存无效,而只需使已更改的实体无效。在不支持有效负载的旧版本中,您可以将无效的条目添加到带有时间戳的日志表中,当您的帮助程序类获得时,该日志表将向其查询NOTIFY
,或者仅使整个缓存无效。
现在,您的帮助程序类LISTEN
基于NOTIFY
触发器发送的事件。收到NOTIFY
事件后,它可使单个缓存条目无效(请参见下文),或刷新整个缓存。您可以使用PgJDBC的监听/通知支持来监听来自数据库的通知。您将需要解开所有管理java.sql.Connection
到底层PostgreSQL实现的连接池,以便可以将其强制转换org.postgresql.PGConnection
并调用getNotifications()
它。
一种替代LISTEN
和NOTIFY
,你可以轮询计时器的更改日志表,以及对问题表追加改变行ID和变化时间戳来更改日志表的触发器。这种方法将是可移植的,除了每种DB类型都需要不同的触发器外,这种方法效率低下且不及时。这将需要频繁的低效轮询,并且仍然存在侦听/通知方法不需要的时间延迟。在PostgreSQL中,您可以使用UNLOGGED
表来减少这种方法的成本。
回答:
EclipseLink / JPA有几个缓存级别。
第一级缓存处于该EntityManager
级别。如果实体附加到EntityManager
通过persist(...)
,merge(...)
,find(...)
,等,则EntityManager
需要返回_该实体的同一实例_ 时,它是在同一会话内再次访问,不管是不是你的应用程序仍然有它的引用。如果您的数据库内容此后发生更改,则此附加实例将不是最新的。
第二级高速缓存(是可选的)处于该EntityManagerFactory
级别,并且是更传统的高速缓存。尚不清楚是否启用了二级缓存。检查您的EclipseLink日志和您的persistence.xml
。您可以使用EntityManagerFactory.getCache()
;
访问第二级缓存。见Cache
。
@thedayofcondor显示了如何使用以下方法刷新第二级缓存:
em.getEntityManagerFactory().getCache().evictAll();
但您也可以通过evict(java.lang.Class cls, java.lang.Object
primaryKey)调用将单个对象逐出:
em.getEntityManagerFactory().getCache().evict(theClass, thePrimaryKey);
您可以从@Startup
@Singleton
NOTIFY
侦听器使用它来仅使那些已更改的条目无效。
1级缓存并不是那么容易,因为它是应用程序逻辑的一部分。您将要了解EntityManager
,附加和分离的实体等的工作方式。一种选择是始终对表使用分离的实体,在EntityManager
获取实体时在其中使用新的实体。
对处理实体管理器的缓存无效进行了有益的讨论。但是,EntityManager
缓存不太可能是您的问题,因为RESTful
Web服务通常使用短EntityManager
会话来实现。仅当您使用扩展的持久性上下文,或者正在创建和管理自己的EntityManager
会话而不是使用容器管理的持久性时,才可能出现问题。
以上是 后端数据库异步更改时如何刷新JPA实体? 的全部内容, 来源链接: utcz.com/qa/408852.html