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

Posted

技术标签:

【中文标题】Hibernate 4 字节码增强不适用于脏检查优化【英文标题】:Hibernate 4 bytecode enhancement not working for dirty checking optimization 【发布时间】:2014-09-06 16:34:19 【问题描述】:

我使用的是 Hibernate 4.3.6,并使用了最新的 Maven bytecode enhancement 来检测所有实体的自我肮脏意识。

我添加了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>

我看到我的实体正在得到增强:

@Entity
public class EnhancedOrderLine
implements ManagedEntity, PersistentAttributeInterceptable, SelfDirtinessTracker

    @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;
  private Long number;
  private String orderedBy;
  private Date orderedOn;

  @Transient
  private transient PersistentAttributeInterceptor $$_hibernate_attributeInterceptor;

  @Transient
  private transient Set $$_hibernate_tracker;

  @Transient
  private transient CollectionTracker $$_hibernate_collectionTracker;

  @Transient
  private transient EntityEntry $$_hibernate_entityEntryHolder;

  @Transient
  private transient ManagedEntity $$_hibernate_previousManagedEntity;

  @Transient
  private transient ManagedEntity $$_hibernate_nextManagedEntity;

  ...

调试时,我正在检查org.hibernate.event.internal.DefaultFlushEntityEventListener#dirtyCheck方法:

        if ( entity instanceof SelfDirtinessTracker ) 
            if ( ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes() ) 
                dirtyProperties = persister.resolveAttributeIndexes( ( (SelfDirtinessTracker) entity ).$$_hibernate_getDirtyAttributes() );
            
        

$$_hibernate_hasDirtyAttributes() 总是返回 false

这是因为$$_hibernate_attributeInterceptor总是为null,所以在设置任意属性时:

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;

因为$$_hibernate_getInterceptor() 为空,trackChange 将被绕过,因此字节码增强不会解决脏属性,将使用默认的深度比较算法。

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

【问题讨论】:

那个增强是字节码增强你是怎么得到EnhancedOrderLine类的源代码的。 关注问题开头的 JIRA 问题链接。 当我查看您提到的链接时,我看到那些人在编译阶段使用它。你能在编译测试阶段再试一次吗? 好的。我会试一试,然后回复你。 那是因为我也写了那篇文章 ;) 【参考方案1】:

Hibernate 5 fixes this issue 现在对 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。

【讨论】:

嗨 Mihalcea,我正在尝试使用 Hibernate 5 为我的模型类(在 XML 映射中)做字节码增强,但不能像上面那样转换模型类。我在这里遗漏了什么还是字节码增强仅适用于注释? 据我所知,它也应该适用于 XML 映射。 我尝试了所有选项,但没有运气。我看到了您的示例并尝试了相同的方法,但 Hibernate 5 版本没有运气。 奇怪。它在my repo 上运行良好。 它适用于注解,但不适用于 XML 映射。我的项目表映射在 XML 文件中定义。【参考方案2】:

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

    通过将@EntityListeners(YourListener.class) 添加到实体来注册实体侦听器 将所有@Pre/@Post(例如@PrePersist 等)方法的实现添加到您的YourListener.class 中,您可以在其中检查实体是否是PersistentAttributeInterceptable 的实例,如果它只是调用$$_hibernate_setInterceptor在它上面有一个自定义的PersistentAttributeInterceptor,它只返回新值(特定行为可能需要针对一般用途进行改进,我不确定,但它足以在我的简单测试中捕获它 - 你了解更多关于一般用途拦截器的情况比我还好)。

针对明显是错误的问题的 hack 解决方案。

【讨论】:

我对 Hibernate 的内置支持更感兴趣。您可以自定义脏检查,但这不是我要求的。 我想说我只是在配置 Hibernate 以确保增强的实体有一个非常简单的拦截器,增强的代码依赖于它的存在。我没有看到我在哪里自定义脏检查。这仍将由 $$_hibernate_getDirtyAttributes/$$_hibernate_hasDirtyAttributes 处理,不是吗?正如您所指出的,增强的代码需要一个拦截器才能工作。可能有另一种方法可以配置 Hibernate 来提供它,但通过实体注释提供它只是另一种方式。 我不明白你的意思。拦截器不需要处理任何脏检查,据我所知,您可以将其设置为始终返回新值。然后增强的脏检查正常工作。通过注释配置一个简单的虚拟拦截器,增强的脏检查工作。我以为这就是你想要的。否则,这个问题只是一个错误报告:“增强的代码需要一个拦截器——它不应该或者 Hibernate 应该提供一个虚拟的,而不是通过注释明确告知”。 我会测试它并告诉你。

以上是关于Hibernate 4 字节码增强不适用于脏检查优化的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot hibernate gradle 字节码增强

Jpa/Hibernate 字节码增强:字段延迟加载

玩 2.4.6,配置测试中字节码增强的构建设置

Hibernate——脏检查和缓存清理机制

关于Hibernate脏检查与快照

Hibernate/MyBatis复习