我有一个使用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
答案 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;查询提示,可用于强制查询刷新托管实体实例。