强制刷新集合JPA entityManager

Dav*_*veB 23 java hibernate seam jpa

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

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

@Override
public List<ItemStatus> refreshList(){
    itemList = itemStatusDAO.getCurrentStatus();
}
Run Code Online (Sandbox Code Playgroud)

使用以下查询

@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();
}
Run Code Online (Sandbox Code Playgroud)

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

@Override
public List<ItemStatus> refreshList(){
    itemStatusDAO.refresh(itemList)
}
Run Code Online (Sandbox Code Playgroud)

调用entityManager.refresh(),这应该从数据库刷新但是javax.ejb.EJBTransactionRolledbackException: Entity not managed当我使用它时我得到一个异常,通常我会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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

sha*_*kan 28

你在这里有两个不同的问题.让我们先轻松一下.


javax.ejb.EJBTransactionRolledbackException:未管理的实体

List查询返回的对象本身不是一个Entity,所以你不能这样.refresh做.事实上,这就是异常所抱怨的.你要求EntityManager用一个根本不知道的对象做一些事情Entity.

如果你想要.refresh一堆东​​西,.refresh可以单独遍历它们.


刷新ItemStatus列表

您正在Session以一种从您的问题中不期望的方式与Hibernate的级别缓存进行交互.来自Hibernate文档:

对于附加到特定会话的对象(即,在会话范围内)... Hibernate保证数据库标识的JVM标识.

这样做的影响Query.getResultList()是您不一定能恢复数据库的最新状态.

Query你运行确实是越来越匹配查询实体ID列表.Session缓存中已存在的任何ID 与已知实体匹配,而未根据数据库状态填充任何非ID.根本不会从数据库刷新先前已知的实体.

这意味着,在Query同一事务中两次执行from之间,某个数据在特定已知实体的数据库中发生了变化的情况下,第二个查询将不会获取该更改.但是,它会选择一个全新的ItemStatus实例(除非您使用的是查询缓存,我认为您不是这样).

简而言之:对于Hibernate,无论何时,只要您想在一个事务中加载实体,然后从数据库中获取对该实体的其他更改,您必须明确.refresh(entity).

你想如何处理这个问题取决于你的用例.我可以想到两个选项:

  1. 必须让DAO与交易的生命周期挂钩,并懒洋洋地初始化List<ItemStatus>.后续调用DAO.refreshList迭代遍历List.refresh(status).如果您还需要新添加的实体,您应该运行Query刷新知ItemStatus对象.
  2. 开始新的交易.听起来像是与@Perception的聊天,尽管这不是一个选项.

一些额外的说明

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

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

org.hibernate.cacheMode = REFRESH这是Hibernate 二级缓存的指令.如果打开了二级缓存,并且您发出了来自不同事务的两个查询,那么您将在第二个查询中获得过时数据,并且此指令可以解决问题.但是,如果您在两个查询中处于同一个Session中,则只会使用二级缓存来避免数据库加载新的实体Session.