Hibernate Envers:初始化 Envers 代理

Posted

技术标签:

【中文标题】Hibernate Envers:初始化 Envers 代理【英文标题】:Hibernate Envers: Initializing Envers Proxies 【发布时间】:2011-07-12 18:43:37 【问题描述】:

在 Hibernate Envers 中,实体的所有相关集合都是延迟加载的,而不管设置了什么获取类型。因此,当对具有其他实体集合的实体进行审计查询时(当然都是经过审计的),该集合最初是SetProxy(在调试时可以看到)。

那么,我该如何初始化那个代理呢?使用 Hibernate.initialize() 没有效果(我怀疑是因为 Hibernate 和 Envers 使用不同的代理对象)。我知道我可以通过迭代它的项目来初始化集合,但这对我来说不是一个选择,因为我在一个实体中有多个集合,更不用说维护问题了。

我需要立即初始化它们,因为我稍后会在 Hibernate 会话已经关闭(将域对象转换为 dtos)时访问集合。

我使用的是 Hibernate 3.5.6。

【问题讨论】:

我也无法让这玩意儿工作! Envers 似乎忽略了 fetch 计划!我有几个实体设置为使用 SELECT 获取模式急切获取(因为我知道它们始终位于二级缓存中 - 只读且永久)。渲染 JSP 时出现延迟初始化错误。痛苦的你知道什么。 :( 咕噜! 【参考方案1】:

您的设计有问题。

如果您需要在拦截器中初始化它们(我怀疑 Envers 通过拦截休眠调用来工作),这意味着您需要事先了解您的域模型。审计应该完全独立于领域建模。

话虽如此,您可以使用一些通用反射方法来滚动您自己的初始化程序来迭代集合,或者您可以使用 Open-Session-In-View 模式并将其调整为与 Envers 一起使用(即,在您的拦截器内部)。

请记住,访问这些项目可能会触发其他查询,如果您分析日志,这可能会造成混淆。


编辑: hibernate 似乎有 fetch 配置文件,它可以让您在运行时选择一个 fetch 计划。 请参阅此SO question 和docs。

【讨论】:

我不确定您所说的“在拦截器中初始化它们”是什么意思。毕竟,我得到了 Envers API 应该得到的域对象(通过 AuditReader 执行 AutitQuery)。但是,我的问题确实是一个未解决的问题 (opensource.atlassian.com/projects/hibernate/browse/HHH-3552),我后来才发现。 遇到了同样的问题。你最后是怎么解决的? 有一些解决方法。您必须在集合上致电size()。然后将初始化 Envers 代理对象。【参考方案2】:

显然,这是 Hibernate Envers 的一个未解决问题。他们的 JIRA 中已经存在一个问题:https://hibernate.atlassian.net/browse/HHH-3552。随意投票,也许它会加快速度,当他们看到有人希望解决这个问题时;)

在 Envers 团队解决此问题之前,有一个对我有用的解决方法:在集合上调用 size() 会初始化代理对象。

【讨论】:

Hibernate.initialize() 对我们也不起作用。只有#size() 救了我们。【参考方案3】:

到目前为止,我发现初始化 Envers 代理的最佳解决方法是使用 Dozer。将 Envers 返回的审计实体映射到自身会强制初始化。

例如:

    // Assuming you have an initialized EntityManager in entityManager & 
    // id contains your entity id..

    List<Object[]> auditList = (List<Object[]>)AuditReaderFactory.
                                   get(entityManager).
                                   createQuery().
                                   forRevisionsOfEntity(Foo.class, false, true).
                                   add(AuditEntity.id().eq(id)).
                                   getResultList();

    // Use a singleton in production apps instead...
    DozerBeanMapper mapper = new DozerBeanMapper();

    for(Object[] audit : auditList) 
        audit[0] = mapper.map(audit[0], Foo.class);
    

    // The proxies in the Foo instances in auditList are now initialized

我对这个解决方案不太满意,但我更喜欢它而不是通过手动触摸集合来初始化代理。希望有人提出更好的替代方案或 HHH-3552 得到修复!

【讨论】:

很好的解决方案,但要注意由于未能为整个对象图的所有类中的所有属性观察正确的 getter 和 setter 命名而导致的错误。一个示例错误可能是布尔属性,例如***.com/questions/5322648/…

以上是关于Hibernate Envers:初始化 Envers 代理的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate Envers

Hibernate Envers @NotAudited 注解

审计没有 Hibernate Envers 的 java 实体

在 Spring Hibernate java 项目中使用“Envers”审计表

审计:对子修改的父实体修订(Javers/Envers/... + Hibernate)

Hibernate Envers 修改了标志行为