后端数据库异步更改时如何刷新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(),我从findNetBeans生成的每个方法中调用。什么通常发生是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连接,并使用LISTENNOTIFY处理高速缓存失效。

:这是另一种有趣的方法,使用pgq和一组worker进行失效。

回答:

在要更新的表上添加一个触发器,该触发器在NOTIFY实体更新时发送一个。在PostgreSQL

9.0及更高版本上,它NOTIFY可以包含有效负载,通常是行ID,因此您不必使整个缓存无效,而只需使已更改的实体无效。在不支持有效负载的旧版本中,您可以将无效的条目添加到带有时间戳的日志表中,当您的帮助程序类获得时,该日志表将向其查询NOTIFY,或者仅使整个缓存无效。

现在,您的帮助程序类LISTEN基于NOTIFY触发器发送的事件。收到NOTIFY事件后,它可使单个缓存条目无效(请参见下文),或刷新整个缓存。您可以使用PgJDBC的监听/通知支持来监听来自数据库的通知。您将需要解开所有管理java.sql.Connection到底层PostgreSQL实现的连接池,以便可以将其强制转换org.postgresql.PGConnection并调用getNotifications()它。

一种替代LISTENNOTIFY,你可以轮询计时器的更改日志表,以及对问题表追加改变行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@SingletonNOTIFY侦听器使用它来仅使那些已更改的条目无效。

1级缓存并不是那么容易,因为它是应用程序逻辑的一部分。您将要了解EntityManager,附加和分离的实体等的工作方式。一种选择是始终对表使用分离的实体,在EntityManager获取实体时在其中使用新的实体。

对处理实体管理器的缓存无效进行了有益的讨论。但是,EntityManager缓存不太可能是您的问题,因为RESTful

Web服务通常使用短EntityManager会话来实现。仅当您使用扩展的持久性上下文,或者正在创建和管理自己的EntityManager会话而不是使用容器管理的持久性时,才可能出现问题。

以上是 后端数据库异步更改时如何刷新JPA实体? 的全部内容, 来源链接: utcz.com/qa/408852.html

回到顶部