Java Hibernate 4字节码增强不适用于脏检查优化

Java Hibernate 4字节码增强不适用于脏检查优化,java,hibernate,orm,byte-code-enhancement,dirty-checking,Java,Hibernate,Orm,Byte Code Enhancement,Dirty Checking,我正在使用Hibernate 4.3.6,并且我使用了最新的工具来检测所有实体的自我污染意识 我添加了maven插件: <build> <plugins> <plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hibernate-enhance-maven-plugin<

我正在使用Hibernate 4.3.6,并且我使用了最新的工具来检测所有实体的自我污染意识

我添加了maven插件:

<build>
    <plugins>
        <plugin>
            <groupId>org.hibernate.orm.tooling</groupId>
            <artifactId>hibernate-enhance-maven-plugin</artifactId>
            <executions>
                <execution>
                    <phase>process-test-resources</phase>
                    <goals>
                        <goal>enhance</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
调试时,我正在检查
org.hibernate.event.internal.DefaultFlushentyEventListener#dirtyCheck
方法:

        if ( entity instanceof SelfDirtinessTracker ) {
            if ( ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes() ) {
                dirtyProperties = persister.resolveAttributeIndexes( ( (SelfDirtinessTracker) entity ).$$_hibernate_getDirtyAttributes() );
            }
        }
$$\u hibernate\u hasDirtyAttributes()
总是返回false

这是因为
$\u hibernate\u attributeInterceptor
始终为空,因此在设置任何属性时:

private void $$_hibernate_write_number(Long paramLong)
{
 if (($$_hibernate_getInterceptor() == null) || ((this.number == null) || (this.number.equals(paramLong))))
  break label39;
 $$_hibernate_trackChange("number");
 label39: Long localLong = paramLong;
 if ($$_hibernate_getInterceptor() != null)
  localLong = (Long)$$_hibernate_getInterceptor().writeObject(this, "number", this.number, paramLong);
 this.number = localLong;
}
由于
$$\u hibernate\u getInterceptor()
为空,因此将绕过trackChange,因此字节码增强不会解决脏属性,将使用默认的深度比较算法


我错过了什么?如何正确设置
$\u hibernate\u attributeInterceptor
,以便字节码检测方法跟踪脏属性?

我不知道它是否能在所有情况下为您提供正确的行为,但您通常可以使脏检查正常工作(至少根据我测试它的一些基本代码)执行以下操作:

  • 通过向实体添加
    @EntityListeners(YourListener.class)
    来注册实体侦听器
  • 为所有
    @Pre
    /
    @Post
    添加实现(例如
    @PrePersist
    等)方法,其中检查实体是否是
    PersistentAttributeInterceptable
    的实例,以及是否只是在其上使用仅返回新值的自定义
    PersistentAttributeInterceptor
    调用
    $\u hibernate\u setInterceptor
    (我不确定,这种特殊的行为可能需要改进以用于一般用途,但它足够好,可以在我的简单测试中捕捉到它——你比我更了解拦截器的一般用例)
  • 显然是一个bug的黑客解决方案。

    现在setter的脏检查如下所示:

    public void $$_hibernate_write_title(String paramString)
    {
        if (!EqualsHelper.areEqual(this.title, paramString)) {
          $$_hibernate_trackChange("title");
        }
        this.title = paramString;
    }
    
    public void $$_hibernate_trackChange(String paramString)
    {
        if (this.$$_hibernate_tracker == null) {
          this.$$_hibernate_tracker = new SimpleFieldTracker();
        }
        this.$$_hibernate_tracker.add(paramString);
    }
    

    因此,解决方案是升级到Hibernate 5。

    是一种字节码增强。您是如何获得类增强程序的源代码的。请遵循问题开头的JIRA问题链接。当我看到您提到的链接时,我看到那些人在编译阶段使用它。您能在编译测试阶段再试一次吗?好的。我将我试着回你电话,因为那篇文章也是我写的;)我更感兴趣的是Hibernate对此的内置支持。你可以自定义脏检查,但这不是我要求的。我想说我只是配置Hibernate以确保增强实体有一个非常简单的拦截器,这是增强代码依赖的。我看不到我在哪里自定义脏检查。这将仍然由$$\u hibernate\u getDirtyAttributes/$$\u hibernate\u hasDirtyAttributes处理,不是吗?正如您指出的,增强的代码需要一个拦截器才能工作。可能有另一种方法配置hibernate来提供拦截器,但通过实体注释提供它只是另一种方法。我不理解您的观点。拦截器不需要处理任何脏检查,就我所知,您可以将其设置为始终返回新值。然后,增强的脏检查可以正常工作。通过通过注释配置一个简单的虚拟拦截器,增强的脏检查可以工作。我想这正是您想要的。否则,这个问题只是一个bug报告:“增强的代码需要一个拦截器——要么它不应该,要么Hibernate应该提供一个伪代码,而不需要通过注释明确地告诉它。”我会测试它并让您知道。嗨,Mihalcea,我正在尝试对我的模型类(在XML映射中)进行字节码增强使用Hibernate 5,但无法转换上述模型类。我是否遗漏了任何内容,或者字节码增强仅适用于注释?据我所知,它也适用于XML映射。我尝试了所有选项,但没有成功。我看到了您的示例,并尝试了相同的方法,但Hibernate 5版本没有成功。奇怪。它可以工作很好。它适用于注释,但不适用于XML映射。我的项目表映射是在XML文件中定义的。
    public void $$_hibernate_write_title(String paramString)
    {
        if (!EqualsHelper.areEqual(this.title, paramString)) {
          $$_hibernate_trackChange("title");
        }
        this.title = paramString;
    }
    
    public void $$_hibernate_trackChange(String paramString)
    {
        if (this.$$_hibernate_tracker == null) {
          this.$$_hibernate_tracker = new SimpleFieldTracker();
        }
        this.$$_hibernate_tracker.add(paramString);
    }