当前位置: 首页 > 面试题库 >

后端数据库异步更改时如何刷新JPA实体?

赏夕
2023-03-14
问题内容

我有一个带有某些表和视图的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 @Singleton NOTIFY侦听器使用它来仅使那些已更改的条目无效。

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

对处理实体管理器的缓存无效进行了有益的讨论。但是,EntityManager缓存不太可能是您的问题,因为RESTful
Web服务通常使用短EntityManager会话来实现。仅当您使用扩展的持久性上下文,或者正在创建和管理自己的EntityManager会话而不是使用容器管理的持久性时,才可能出现问题。



 类似资料:
  • 我有一个PostgreSQL 8.4数据库,其中有一些表和视图,它们实际上是一些表上的联接。我使用NetBeans7.2(如本文所述)创建基于REST的服务,这些服务派生自这些视图和表,并将它们部署到Glassfish 3.1.2.2服务器上。 还有另一个进程,它异步更新用于构建视图的某些表中的内容。我可以直接查询视图和表,并看到这些更改已经正确发生。但是,当从基于REST的服务中提取时,这些值与

  • 问题内容: 发生更改时,如何检测对数据库的最新更新并以静默方式刷新页面? 假设数据库访问如下所示: 任何想法和样品将不胜感激。谢谢。 问题答案: 这就是我最近使用jQuery实现解决方案的方式。 每当发生 重大 更新时,PHP都会在数据库中增加一个字段。 最初加载页面时,使用数据库中的数字填充JavaScript变量:

  • 之前name的更新是同步更新的没有问题现在需要走接口,等接口返回后再更新,我试过在这个文件中调用dispath但是调用报错,请问下这种异步的数据该怎么更新

  • 我从这个问题中编辑了一个柱塞

  • 未解决最初的关闭原因 让我们假设出现这种情况: 为什么? 有没有一种方法可以在Spring-Data中“同步”(开箱即用)对象(或者强制刷新)?还是我在要求奇迹?我不是讽刺,但也许我不是那么内行,也许(或者可能)是我的无知。如果是这样,请解释我为什么,并(如果你想)分享一些关于这个惊人的框架的先进知识。

  • 这个问题几乎说明了一切。使用JPARepository,我如何更新一个实体? JPARepository只有一个save方法,它并不告诉我它实际上是create还是update。例如,我向数据库用户插入一个简单的对象,它有三个字段:、和: 然后我只需调用,此时它实际上是对数据库的插入: 到目前为止还不错。现在我想更新这个用户,比如说改变他的年龄。为此,我可以使用查询,无论是QueryDSL还是Na