我有一个使用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),但都没有用。
我在创建的实体(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()。如果会话没有更新,而我单击了该按钮,它将始终更新为最新数据。
谢谢,佐博
发布于 2016-10-13 11:58:27
会话之间没有锁定,所以我不太确定你的意思。在大多数JPA提供程序文档中,建议使用乐观锁定来防止覆盖陈旧的数据。
您还没有展示或指定如何获取EntityManager,或者它的生存期有多长,但是有两个级别的缓存。第一个是EntityManager本身,它用于跟踪更改以管理实体并维护其身份。JPA允许但不强制要求在EntityManagerFactory级别共享的第二级缓存。这第二级缓存是javax.persistence.cache.storeMode的目标-它控制从共享缓存中拉出实体时发生的事情。如果实体已经加载到一级缓存中,因为这意味着表示事务作用域,它们将按原样返回,保留应用程序可能已经做出的任何非同步更改,并且需要JPA提供程序进行跟踪。
JPA强制刷新托管实体的唯一方法是调用em.refresh(),不过也可以通过调用em.clear,然后使用javax.persistence.cache.storeMode刷新提示重新读取实体来实现。EclipseLink还有一个"eclipselink.refresh“查询提示,可用于强制查询刷新托管实体实例。
https://stackoverflow.com/questions/40007066
复制相似问题