JPA实体使用多个会话与数据库不同步

时间:2016-10-12 19:44:31

标签: jpa java-ee eclipselink

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

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

当我检查价格时,我在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有状态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),但无济于事。

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

<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()。如果会话未更新,我单击按钮,它将始终更新为最新数据。

谢谢, Zobbo

1 个答案:

答案 0 :(得分:1)

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

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

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