Search code examples
javahibernateormbyte-code-enhancementdirty-checking

Hibernate 4 bytecode enhancement not working for dirty checking optimization


I am using the Hibernate 4.3.6 and I made use of the latest Maven bytecode enhancement to instrument all entities for self dirtiness awareness.

I added the maven plugin:

<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>

and I see my entities are being enhanced:

@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;

  ...

While debugging, I am checking org.hibernate.event.internal.DefaultFlushEntityEventListener#dirtyCheck method:

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

and the $$_hibernate_hasDirtyAttributes() always returns false.

This is because $$_hibernate_attributeInterceptor is always null, so when setting any property:

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;
}

because the $$_hibernate_getInterceptor() is null the trackChange will be bypassed, hence the bytecode enhancement will not resolve the dirty properties and the default deep-comparison algorithm will be used.

What am I missing? How can I get the $$_hibernate_attributeInterceptor to be properly set so that the dirty properties are tracked by the bytecode instrumentation methods?


Solution

  • Hibernate 5 fixes this issue and now the dirty checking for a setter looks like this:

    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);
    }
    

    So, the solution is an upgrade to Hibernate 5.