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

标签 java postgresql jpa netbeans glassfish

我有一个 PostgreSQL 8.4 数据库,其中包含一些表和 View ,这些表和 View 本质上是对某些表的连接。我使用 NetBeans 7.2(如 here 所述)创建从这些 View 和表派生的基于 REST 的服务,并将它们部署到 Glassfish 3.1.2.2 服务器。

还有另一个进程异步更新一些用于构建 View 的表中的内容。我可以直接查询 View 和表并查看这些更改是否正确发生。但是,从基于 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抛出说明类似 Can 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]. 的东西

所以我正在寻找一些关于如何正确刷新与 View 关联的实体的建议,以便它是最新的。

更新:原来我对潜在问题的理解是不正确的。和another question I posted earlier有点关系,即 View 没有可用作唯一标识符的单个字段。 NetBeans 要求我选择一个 ID 字段,所以我只选择了应该是多部分键的一部分。这表现出具有特定 ID 字段的所有记录都相同的行为,即使数据库具有具有相同 ID 字段的记录但其余记录不同。 JPA 只是查看我告诉它的唯一标识符并简单地提取它找到的第一条记录。

我通过添加唯一标识符字段解决了这个问题(从来没有能够让多部分键正常工作)。

最佳答案

我建议添加一个 @Startup @Singleton与 PostgreSQL 数据库建立 JDBC 连接并使用 LISTEN and NOTIFY 的类处理缓存失效。

更新 :Here's another interesting approach, using pgq and a collection of workers for invalidation .

失效信号

在正在更新的表上添加一个触发器,发送 NOTIFY每当更新实体时。在 PostgreSQL 9.0 及以上版本 NOTIFY可以包含有效负载,通常是行 ID,因此您不必使整个缓存失效,只需更改已更改的实体即可。在不支持有效负载的旧版本上,您可以将无效条目添加到带时间戳的日志表中,您的助手类在获得 NOTIFY 时查询该日志表。 ,或者只是使整个缓存无效。

您现在的助手类(class) LISTEN s 上 NOTIFY触发器发送的事件。当它收到 NOTIFY事件,它可以使单个缓存条目无效(见下文),或刷新整个缓存。您可以使用 PgJDBC's listen/notify support 监听来自数据库的通知。 .您将需要解开任何管理的连接池java.sql.Connection进入底层 PostgreSQL 实现,以便您可以将其转换为 org.postgresql.PGConnection并调用 getNotifications()在上面。

替代 LISTENNOTIFY ,您可以在计时器上轮询更改日志表,并在问题表上设置触发器,将更改的行 ID 和更改时间戳附加到更改日志表。除了需要为每种 DB 类型使用不同的触发器之外,这种方法将是可移植的,但它效率低下且不及时。它将需要频繁的低效轮询,并且仍然具有监听/通知方法没有的时间延迟。在 PostgreSQL 中,您可以使用 UNLOGGED表以稍微降低这种方法的成本。

缓存级别

EclipseLink/JPA 有几个级别的缓存。

一级缓存位于 EntityManager 等级。如果实体附加到 EntityManager来自 persist(...) , merge(...) , find(...)等,然后是 EntityManager在同一 session 中再次访问该实体时,无论您的应用程序是否仍然引用它,都需要返回该实体的相同实例。如果您的数据库内容已更改,则此附加实例将不会是最新的。

二级缓存(可选)位于 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监听器仅使那些已更改的条目无效。

一级缓存不是那么容易,因为它是应用程序逻辑的一部分。您会想了解 EntityManager ,附加和分离的实体等工作。一种选择是始终对有问题的表使用分离的实体,您使用新的 EntityManager每当您获取实体时。这个问题:

Invalidating JPA EntityManager session

有一个关于处理实体管理器缓存失效的有用讨论。但是,不太可能是 EntityManager缓存是您的问题,因为 RESTful Web 服务通常使用短 EntityManager 实现 session 。如果您正在使用扩展的持久性上下文,或者您正在创建和管理自己的 EntityManager,这可能只是一个问题。 session 而不是使用容器管理的持久性。

关于java - 后端数据库异步更改时如何刷新JPA实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13258976/

相关文章:

javascript - 使用 SEQUELIZE 在 iLike 中使用 CAST 或 CONVERT 进行选择

java - 数据库如何通过 Hibernate 与 jpa 注解的 pojo 类交互?

java - JPA/Hibernate 具有共享主键的单向一对一映射

java - 如何检测SpringBoot定时任务是否已经死锁?

java - 查询从 2 个时间戳之间的数据库中选择数据,异常 : time_bucket function does not exists?

java - Switch 与 ArrayList Java

sql - Postgres 9.0 慢查询

java - Kotlin - 调用 getter 时未加载 HibernateProxy

java - 将字符串从一个子类传递到另一个子类

java - 连接到另一个类时 AWT-EventQueue-0 java.lang.NullPointerException