强制刷新收集JPA entityManager [英] Force refresh of collection JPA entityManager

查看:174
本文介绍了强制刷新收集JPA entityManager的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在JPA中使用SEAM(作为Seam Managed Persistance Context实现),在我的支持bean中,我将一组实体(ArrayList)加载到支持bean中。



<如果一个不同的用户修改了一个不同的会话中的一个实体,我希望这些更改传播到会话中的集合,我有一个方法 refreshList(),并且已尝试以下...

  @Override 
public List< ItemStatus> refreshList(){
itemList = itemStatusDAO.getCurrentStatus();

$ / code>

通过以下查询

  @SuppressWarnings(unchecked)
@Override
public List< ItemStatus> getCurrentStatus(){
String s =SELECT DISTINCT iS FROM ItemStatus iS;
s + =ORDER BY iS.dateCreated ASC;
Query q = this.getEntityManager()。createQuery(s);
return q.getResultList();

$ / code>

重新执行查询,这只会返回我已有的相同数据(我假设它使用的是第一级缓存而不是点击数据库)

  @Override 
public List< ItemStatus> refreshList(){
itemStatusDAO.refresh(itemList)
}

调用 entityManager.refresh(),这应该从数据库刷新,但是我得到一个 javax.ejb.EJBTransactionRolledbackException:未管理的实体当我使用这个例外时,通常我会在调用.refresh()之前使用 entityManager.findById(entity.getId)来确保它连接到PC,但是因为我正在刷新一个实体集合,我不能这样做。

这似乎是一个非常简单的问题,我不相信有没有办法强制JPA / hibernate绕过缓存并命中数据库?!



更新测试用例:



我使用两种不同的浏览器(1和2)加载相同的网页,我做了一个修改1,它更新了其中一个ItemStatus实体的布尔属性,视图刷新为1以显示更新后的属性,我通过PGAdmin检查数据库,并且该行已更新。然后我按浏览器2刷新和属性尚未更新

我尝试使用以下方法合并所有实体,然后调用.refresh,但实体仍然不从数据库更新。

  @Override 
public void mergeCollectionIntoEntityManager(List< T> entityCollection){
for(T entity:entityCollection){
if(!this.getEntityManager()。contains(entity)){
this.getEntityManager()。refresh(this.getEntityManager()。merge(entity)) ;




解决方案

你在这里有两个单独的问题。让我们先轻松一点。




javax.ejb.EJBTransactionRolledbackException:实体无法管理



该查询返回的对象的 List 不是本身 Entity ,所以你不能 .refresh 它。事实上,这是例外情况的抱怨。您要求 EntityManager 对一个不是已知实体的对象进行操作。



如果你想要 .refresh 一堆东西,遍历它们并且 .refresh 它们。






刷新ItemStatus列表



您正在与Hibernate的 Session -level缓存进行交互,从您的问题出发,您并不期待。从 Hibernate文档 :b
$ b


对于附加到特定会话的对象(即,在
Session的范围内)数据库身份由
Hibernate保证。

这对 Query.getResultList() 是你不一定会取回数据库的最新状态。

您运行的 Query 确实获取与该查询匹配的实体ID的列表。已存在于 Session 缓存中的任何ID都与已知实体匹配,而任何不是基于数据库状态的ID填充。之前已知的实体根本不从数据库中刷新。



这意味着,在两次执行查询来自同一个事务中,某个已知实体的某些数据在数据库中发生了变化,第二个查询将不是选取该更改。但是,它会选择一个全新的 ItemStatus 实例(除非您使用查询缓存,我假设你没有)。



<长话短说:在Hibernate中,只要你想在单个事务中加载一个实体,然后从数据库中获取对该实体的额外修改,就必须显式 .refresh(entity)



如何处理这个问题取决于您的用例。我可以从蝙蝠身上想到两个选择:


  1. 必须将DAO绑定到事务的生命周期,并且懒惰地初始化列表与LT; ItemStatus> 。随后调用 DAO.refreshList 遍历 List .refresh(status) code>。如果您还需要新添加的实体,则应该运行 Query

  2. 开始新的交易。听起来就像你与@Perception的聊天,尽管这不是一个选项。






一些附加说明



有关使用查询提示的讨论。这就是为什么他们不工作:



org.hibernate.cacheable = false 这只会在您使用查询缓存,这只在非常推荐特殊情况。即使您正在使用它,它也不会影响您的情况,因为查询缓存包含对象ID,而不是数据。



org.hibernate.cacheMode = REFRESH 这是Hibernate的指令二级缓存。如果第二级缓存已打开,并且您正在发出来自不同事务的两个查询,那么您将在第二个查询中获得陈旧的数据,并且此指令将解决该问题。但是,如果您在两个查询中处于同一个会话中,那么第二级高速缓存将仅用于避免数据库负载为 Session 新增的实体。

I am using SEAM with JPA (implemented as a Seam Managed Persistance Context), in my backing bean I load a collection of entities (ArrayList) into the backing bean.

If a different user modifies one of the entities in a different session I want these changes to be propagated to the collection in my session, I have a method refreshList() and have tried the following...

@Override
public List<ItemStatus> refreshList(){
    itemList = itemStatusDAO.getCurrentStatus();
}

With the following query

@SuppressWarnings("unchecked")
@Override
public List<ItemStatus> getCurrentStatus(){
    String s = "SELECT DISTINCT iS FROM ItemStatus iS ";
    s+="ORDER BY iS.dateCreated ASC";
    Query q = this.getEntityManager().createQuery(s);
    return q.getResultList();
}

Re-executing the query, this just returns the same data I already have (I assume it is using the 1st level cache rather than hitting the database)

@Override
public List<ItemStatus> refreshList(){
    itemStatusDAO.refresh(itemList)
}

Calling entityManager.refresh(), this should refresh from the database however I get a javax.ejb.EJBTransactionRolledbackException: Entity not managed exception when I use this, normally I would use entityManager.findById(entity.getId) before calling .refresh() to ensure it is attached to the PC but as I am refreshing a collection of entities I cant do that.

It seems like quite a simple problem, I cant believe there is no way to force JPA/hibernate to bypass the cache and hit the database?!

UPDATE TEST CASE:

I am using two different browsers (1 and 2) to load the same web page, I make a modification in 1 which updates a boolean attribute in one of the ItemStatus entities, the view is refreshed for 1 to display the updated attribute, I check the database via PGAdmin and the row has been updated. I then press refresh in Browser 2 and the attribute has not been updated

I tried using the following method to merge all entities before calling .refresh, but the entities were still not updated from the database.

@Override
public void mergeCollectionIntoEntityManager(List<T> entityCollection){
    for(T entity: entityCollection){
        if(!this.getEntityManager().contains(entity)){
            this.getEntityManager().refresh(this.getEntityManager().merge(entity));
        }
    }
}

解决方案

You're having two separate problems here. Let's take the easy one first.


javax.ejb.EJBTransactionRolledbackException: Entity not managed

The List of objects returned by that query is not itself an Entity, and so you can't .refresh it. In fact, that's what the exception is complaining about. You're asking the EntityManager to do something with an object that is simply not a known Entity.

If you want to .refresh a bunch of things, iterate through them and .refresh them individually.


Refreshing the list of ItemStatus

You're interacting with Hibernate's Session-level cache in a way that, from your question, you don't expect. From the Hibernate docs:

For objects attached to a particular Session (i.e., in the scope of a Session)... JVM identity for database identity is guaranteed by Hibernate.

The impact of this on Query.getResultList() is that you do not necessarily get back the most up to date state of the database.

The Query you run is really getting a list of entity IDs that match that query. Any IDs that are already present in the Session cache are matched up to known entities, while any IDs that are not are populated based on database state. The previously known entities are not refreshed from the database at all.

What this means is that in the case where, between two executions of the Query from within the same transaction, some data has changed in the database for a particular known entity, the second Query would not pick up that change. It would, however, pick up a brand new ItemStatus instance (unless you were using a query cache, which I assume you're not).

Long story short: With Hibernate, whenever you want to, within a single transaction, load an entity and then pick up additional changes to that entity from the database, you must explicitly .refresh(entity).

How you want to deal with this depends a bit on your use case. Two options I can think of off the bat:

  1. Have are to have the DAO tied to the lifespan of the transaction, and lazily initialize the List<ItemStatus>. Subsequent calls to DAO.refreshList iterate through the List and .refresh(status). If you also need newly added entities, you should run the Query and also refresh the known ItemStatus objects.
  2. Start a new transaction. Sounds like from your chat with @Perception though that that is not an option.


Some additional notes

There was discussion about using query hints. Here's why they didn't work:

org.hibernate.cacheable=false This would only be relevant if you were using a query cache, which is only recommended in very particular circumstances. Even if you were using it though, it wouldn't affect your situation because the query cache contains object IDs, not data.

org.hibernate.cacheMode=REFRESH This is a directive to Hibernate's second-level cache. If the second-level cache were turned on, AND you were issuing the two queries from different transactions, then you would have gotten stale data in the second query, and this directive would have resolved the problem. But if you're in the same Session in the two queries, the second level cache would only come in to play to avoid database loading for entities that are new to this Session.

这篇关于强制刷新收集JPA entityManager的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆