我将SEAM与JPA(实现为Seam托管持久性上下文)一起使用,在后备bean中,我将一组实体(ArrayList)加载到后备bean中。
如果不同的用户修改了另一个会话中的实体之一,而我希望将这些更改传播到会话中的集合中,则我有一种方法refreshList()并尝试了以下方法…
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: Entity not managed当我使用它时会出现异常,通常我会entityManager.findById(entity.getId)在调用.refresh()以确保它已连接到PC之前使用它,但是当我刷新实体集合时,我不能这样做。
entityManager.refresh()
javax.ejb.EJBTransactionRolledbackException: Entity not managed
entityManager.findById(entity.getId)
看来这是一个很简单的问题,我无法相信没有办法强迫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根本未知的对象执行某项操作Entity。
List
Entity
.refresh
EntityManager
如果您想要.refresh一堆东西,请遍历它们并逐一进行.refresh。
刷新ItemStatus的列表
您正在Session以某种您所不希望的方式与Hibernate的- level缓存进行交互。从Hibernate文档:
Session
对于附加到特定会话的对象(即,在会话范围内)…数据库身份的JVM身份由Hibernate保证。
这样做的影响Query.getResultList()是,您不必一定要恢复数据库的最新状态。
Query.getResultList()
在Query您运行确实是越来越匹配查询实体ID列表。Session高速缓存中已经存在的任何ID都会与已知实体匹配,而没有根据数据库状态填充的任何ID。先前已知的实体 根本不会 从数据库中刷新。
Query
这意味着Query在同一事务中两次执行from 之间,如果数据库中某个已知实体的某些数据发生了更改,则第二个Query将 不 接受该更改。但是,它将选择一个全新的ItemStatus实例(除非您使用的是查询缓存,否则我认为您不是)。
ItemStatus
长话短说:使用Hibernate,只要想在单个事务中加载一个实体,然后从数据库中对该实体进行其他更改,就必须显式地.refresh(entity)。
.refresh(entity)
您如何处理此问题取决于您的用例。我可以想到两种选择:
List<ItemStatus>
DAO.refreshList
.refresh(status)
一些附加说明
关于使用查询提示的讨论。这就是为什么它们不起作用的原因:
org.hibernate.cacheable = false 仅在使用查询缓存时才有意义,仅在非常特殊的情况下才建议这样做。即使您正在使用它,也不会影响您的情况,因为查询缓存包含对象ID,而不是数据。
org.hibernate.cacheMode = REFRESH 这是Hibernate 二级缓存的指令。如果打开了第二级缓存,并且您是从不同的事务发出两个查询,那么您将在第二个查询中获得过时的数据,并且此伪指令将解决此问题。但是,如果您在两个查询中处于同一会话中,则只会使用第二级缓存来避免为this的新实体加载数据库Session。