Hibernate 使用旧的封闭 SessionImpl 而不是给定的 SessionImpl

Hibernate uses old closed SessionImpl instead of given SessionImpl

我对 Hibernate Enverse(5.2.0 版-最终版)有疑问。

上下文: 我正在审计一些具有惰性关系的实体。我有一个 jsf 页面,它加载一个实体的一个版本以及该版本的所有关系。那很好用。所以现在我有一个页面显示实体的修订以及该修订的所有关系。在此页面上,我可以打开一个字段集,它会触发 AJAX。在此请求中,我们通过调用 entityManager.merge(entity) 重新附加所有关系,以便能够获取此字段集中的惰性关系。 (EntityManager 是 RequestScoped)

问题: AJAX 是一个新请求。服务器调用 entityManager.merge(entity),强制创建新的 EntityManager(因此创建了新的 org.hibernate.internal.SessionImpl)。在此对象上休眠调用 SessionImpl.merge(...)。但是在方法org.hibernate.internal.AbstractSharedSessionContract.createQuery(String)中使用了另一个SessionImpl对象,这个对象在之前的请求中已经关闭了。这强制执行 java.lang.IllegalStateException: Session/EntityManager is closed.

一句话:虽然创建了一个新的entityManager并且在那个新的entityManger上调用了merge,但是Hibernate之前使用了旧的Session/EntityManager请求。

调试问题发现如下:

我的问题:

非常感谢你的任何想法。我在这个错误上花了几天时间。

Why is the Session arg in o.h.type.CollectionType.getElementIterator not used?

简短的回答是它不是必需的,它只是 8 年前的向后兼容性问题。

长答案是类型系统用于根据用户是否指定了要在 EntityMode.MAPEntityMode.POJO 中操作的会话以及因此需要的类型来实际偏离某些行为知道会话处于什么模式;因此它被通过的原因。

但即使在 2011 年更改时,会话参数只会影响行为 当且仅当 会话在 EntityMode.MAP 中运行时。换句话说,所有其他模式总是直接路由到底层集合 Collection#iterator() 方法。

尽管如此,这对您在 Debug3 屏幕截图中的体验没有任何影响。

Is this a bug in Hibernate?

不,根据我所读到的内容,我相信你是在混淆问题。

在 Hibernate(没有 Envers)中,你基本上可以做到这一点

// Request 1
request1EntityManager = getEntityManager();
sessionScopeEntity = request1EntityManager.find( MyEntity.class, myEntityId );

// Request 2
request2EntityManager = getEntityManager();
sessionScopeEntity = request2EntityManager.merge( sessionScopeEntity );
for ( SomeCollectionItem Item : sessionScopeEntity.getSomeCollection() ) {
  // do things here
}

上述方法有效,因为您将实体与新会话重新关联,新会话又将会话注入到实体维护的所有未初始化代理中。但是您也可以将上面的内容重写为

// Request 1
request1EntityManager = getEntityManager();
sessionScopeEntity = request1EntityManager.find( MyEntity.class, myEntityId );
sessionScopeEntity.getSomeCollection().size() // initialize collection w/request1Session

// Request 2
request2EntityManager = getEntityManager();
for ( SomeCollectionItem Item : sessionScopeEntity.getSomeCollection() ) {
  // do things here
}

不同之处在于集合是在第一个会话中初始化的,因此当您尝试在第二个会话中访问它时,实体不一定需要合并,因为集合不再是代理但实际上填充为一个正常的获取集合将是。

Hibernate 返回的实体实例与 Envers 返回的审计实体实例之间的主要区别是审计实体实例不是托管持久实体。

根据您的情况,您可能决定仅审核实体映射上的部分字段。这就是为什么你不能也不应该对那个实例使用 merge 之类的东西,因为它很容易对你的真实数据造成意想不到的副作用。

如果您打算跨会话传递经过审计的实体实例,我强烈建议您改为考虑在您获取实例的第一个会话中预先初始化您需要的集合,因为目前无法重新-将已审计的实体实例与新会话相关联。