java – 强制刷新收集JPA entityManager

我正在使用带有JPA的SEAM(实现为Seam Managed Persistance Context),在我的支持bean中,我将一个实体集(ArrayList)加载到后备bean中.

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

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

用以下查询

@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();
}

重新执行查询,这只是返回我已经拥有的相同数据(我认为是使用第一级缓存而不是打数据库)

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

调用entityManager.refresh(),这应该从数据库刷新,但是我得到一个javax.ejb.EJBTransactionRolledbackException:实体不管理异常当我使用这个,通常我会使用entityManager.findById(entity.getId)调用之前.refresh()以确保它连接到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:实体未被管理

该查询返回的对象列表本身不是实体,因此您不能.refresh它.事实上,这正是抱怨的例外.您要求EntityManager使用一个不是已知实体的对象执行某些操作.

如果你想要一些东西,请重复一遍,然后逐个刷新它们.

刷新ItemStatus列表

您正在与Hibernate的会话级缓存进行交互,从您的问题,您不期望.从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.

这对Query.getResultList()的影响是,您不一定会收回数据库的最新状态.

您运行的查询真的会获得与该查询匹配的实体ID列表.已存在于会话高速缓存中的任何ID与已知实体相匹配,而根据数据库状态填充的任何ID都不会被填充.以前已知的实体根本不会从数据库刷新.

这意味着在两次执行相同事务之间的Query的情况下,某个特定已知实体的数据库中的某些数据已发生更改时,第二个Query将不会接收到该更改.然而,它会拿起一个全新的ItemStatus实例(除非你使用query cache,我认为你不是).

长篇小说:使用Hibernate,只要您在单个事务中加载一个实体,然后从数据库中提取对该实体的其他更改,则必须明确.refresh(实体).

你如何处理这一点取决于你的用例.两个选项我可以想到蝙蝠:

>将DAO绑定到事务的生命周期,并且懒惰地初始化List< ItemStatus>.对DAO.refreshList的后续调用遍历List和.refresh(status).如果还需要新添加的实体,则应运行查询并刷新已知的ItemStatus对象.
>开始一个新的交易.听起来像你的@Perception聊天,虽然这不是一个选项.

一些额外的笔记

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

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

org.hibernate.cacheMode = REFRESH这是Hibernate的second-level cache的指令.如果二级缓存已打开,并且您正在从不同的事务发出两个查询,那么您将在第二个查询中获得过时的数据,指令会解决问题.但是,如果您在两个查询中处于相同的会话中,则只能在第二级缓存中执行该操作,以避免对本次会话新增的实体进行数据库加载.

转载注明原文:java – 强制刷新收集JPA entityManager - 代码日志