【问题标题】:JPA Entity out of sync with database using multiple sessions使用多个会话的 JPA 实体与数据库不同步
【发布时间】:2016-10-12 19:44:30
【问题描述】:

我有一个使用 Eclipselink 实现的 JPA 的 Java EE 应用程序。我已经使用 ExternalContext 会话映射实现了一个基本的用户登录系统。但是,似乎会话经常与数据库不同步。

基本流程是 1. 用户 A 创建一个 BidOrder 实体。 2. 用户 B 创建一个 AskOrder 实体。 3. 监视器检查两个订单是否匹配,如果匹配,则创建一个 OrderBook 实体 4. 将更改推送给所有用户(使用 Primefaces 5.3 Push)

当我检查价格时,我在我的 SessionScoped 支持 bean 中使用此方法作为我的主视图:

public void findLatestPrices()
 {
    logger.log(Level.INFO, "findLatestPrices with user {0} ",user.getUserId());

    findAllOrderBooks(); 
    latestPricesId = new ArrayList<String>();
    setLatestPricesResults(request.findLatestPrices());
     for (Iterator<OrderBook> it = orderBookResults.iterator(); it.hasNext();) 
     {
        OrderBook orderBook = it.next(); 
        logger.log(Level.INFO, "Found {0} orderbook price", orderBook.getPrice());
     }

     logger.log(Level.INFO, "End of findLatestPrices with user {0} ",user.getUserId());
 }

这调用了我的 RequestScoped Stateful ejb:

public List<OrderBook> findLatestPrices() {
        List<OrderBook> orderBooks;
        List<OrderBook> orderBooksFiltered;

        Map<Member, OrderBook> map = new LinkedHashMap<Member, OrderBook>();

         try {
             orderBooks = em.createNamedQuery(
                     "findAllOrderBooks").setHint("javax.persistence.cache.storeMode", "REFRESH")
                     .getResultList();
             for (Iterator<OrderBook> it = orderBooks.iterator(); it.hasNext();) {
                 OrderBook orderBook = it.next();
                 Member member = orderBook.getBidOrderId().getMember();
                 map.put(member, orderBook);

                    logger.log(Level.INFO, "findLatestPrices orderbook price : {0}", 
                            orderBook.getPrice());
                    logger.log(Level.INFO, "findLatestPrices orderbook bidorder member : {0}", 
                            orderBook.getBidOrderId().getMember().getMemberId());
                    logger.log(Level.INFO, "findLatestPrices orderbook lastupdate : {0}", 
                            orderBook.getLastUpdate());
             }
...}

我在上面的bean中创建EntityManager的方式如下:

@PersistenceContext
private EntityManager em;

从日志记录中我可以看到会话返回的数据与数据库不同步,即当我期望两个结果时会返回单个结果等。如您所见,我已尝试使用 setHint 刷新缓存。我还在我的 OrderBook 实体和 @Cache(refreshAlways=true) 上尝试了 @Cacheable(false) 但无济于事。

我正在创建的实体 (OrderBook) 的 @PostPersist 中发送推送事件。我的 xhtml 页面中的 javascript 事件处理程序然后调用以下远程命令:

<p:remoteCommand name="updateWidget"
             autoRun="false"
             actionListener="#{parliamentManager.findLatestPrices}"
             update="resultDisplay"
             onstart="onStart()"
             oncomplete="onComplete()"
             onsuccess="onSuccess()"
             onerror="onError()">
             <f:actionListener binding="#{parliamentManager.findTraders()}"  />
             <f:actionListener binding="#  {parliamentManager.findPortfolios()}" />
             </p:remoteCommand>

findLatestPrices 的结果似乎通常不包括所有会话的最新 OrderBook 实体。是否有可能实体在调用@PostPersist 时没有立即持久化,研究理论是在实体完全持久化并由JPA 反映之前将推送发送到某些会话?

为了演示,我添加了一个简单的命令按钮来手动调用 updateWidget()。如果会话未更新并且我单击按钮,它总是会更新为最新数据。

谢谢, 佐博

【问题讨论】:

  • 在查询em.getEntityManagerFactory().getCache().evictAll()之前有没有尝试清除缓存
  • 感谢 simdevmon 的回复。我试过了,但不幸的是它没有任何区别。

标签: jpa jakarta-ee eclipselink


【解决方案1】:

会话之间没有锁定,所以我不太清楚你的意思。大多数 JPA 提供程序文档都建议使用乐观锁定来防止覆盖过时的数据。

您还没有显示或指定如何获取 EntityManager,或者它存在多长时间,但是有两个级别的缓存。第一个是 EntityManager 本身,它用于跟踪更改以管理实体并维护其身份。 JPA 允许但不强制要求在 EntityManagerFactory 级别共享的第二级缓存。这个第二级缓存是 javax.persistence.cache.storeMode 的目标——它控制从共享缓存中提取实体时发生的情况。如果实体已经加载到第一级缓存中,因为这意味着表示事务范围,则它们按原样返回,保留应用程序可能已进行的任何未同步更改,并且需要 JPA 提供程序进行跟踪。

JPA 提供强制刷新托管实体的唯一方法是调用 em.refresh(),但也可以通过调用 em.clear 来完成,然后使用 javax.persistence.cache 重新读取实体.storeMode 刷新提示。 EclipseLink 还有一个“eclipselink.refresh”查询提示,可用于强制查询刷新托管实体实例。

【讨论】:

  • 嗨,克里斯,感谢您的回复。我只是使用@PersistenceContext 创建了EntityManager,我已经编辑了我的帖子来说明。
  • 我已经尝试了上述建议(清除缓存,然后使用刷新提示),但没有奏效。我不能真正调用刷新,因为我正在返回一个实体列表,问题是列表中缺少实体。由于 simdevmon 的 evictall 建议也不起作用,我开始怀疑它是否是缓存而不是一些逻辑问题。
  • 然后直接验证数据库中的内容,因为 JPA 无法读取不存在的内容。
  • 谢谢克里斯,虽然我还没有这样做,但我已经为原始问题添加了更多细节。我打算添加 sql 日志,看看我是否能弄清楚发生了什么
  • 您的更新彻底改变了一切。 PostPersist 在插入语句之后执行,但事务尚未提交,因此对其他连接/会话不可见。
猜你喜欢
  • 1970-01-01
  • 2012-01-28
  • 1970-01-01
  • 2021-08-11
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多