34

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?

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911

2 Answers2

4

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.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • Hi Mihalcea, I was trying to do bytecode enhancement for my model classes (which are in XML mappings) with using Hibernate 5 but not able to convert model classes like above one. Am I missing anything here or is byte code enhancement will work only for annotations? – sats Mar 16 '16 at 18:35
  • As far as I know, it should work with XML mappings too. – Vlad Mihalcea Mar 16 '16 at 21:41
  • I tried with all options but no luck. I saw your example and tried the same but no luck for Hibernate 5 version. – sats Mar 17 '16 at 14:41
  • Strange. It works just fine on [my repo](https://github.com/vladmihalcea/high-performance-java-persistence). – Vlad Mihalcea Mar 17 '16 at 17:37
  • It is working for annotations but not for XML mappings. My project table mappings are defined in XML file. – sats Mar 17 '16 at 21:16
  • Add a replicating test case and open a JIRA issue then ;) – Vlad Mihalcea Mar 18 '16 at 06:28
  • Yeah. I did that one. Here is the [link](https://hibernate.atlassian.net/browse/HHH-10622). The defect is assigned but don't see any updates after that. Will update you if I get any update :) – sats Mar 23 '16 at 12:37
2

I don't know if it will give you the correct behaviour in all situations, but you can generally get the dirty checks working (at least according to some skeleton code I tested it with) by doing the following:

  1. Register an entity listener by adding @EntityListeners(YourListener.class) to the entities
  2. Add implementations for all @Pre/@Post (eg @PrePersist etc) methods to your YourListener.class where you check if the entity is an instance of PersistentAttributeInterceptable, and if it is just call $$_hibernate_setInterceptor on it with a custom PersistentAttributeInterceptor that just returns the new values (that particular behaviour may need refined for general use, i'm not sure, but it was good enough to catch it for my simple tests - you know more about general use cases for the interceptor than me).

A hack solution for what is clearly a bug.

user467257
  • 1,714
  • 1
  • 16
  • 19
  • 1
    I am more interested in Hibernate built-in support for that. You can customize the dirty checking but that's not what I asked for. – Vlad Mihalcea Jun 19 '15 at 13:01
  • I would say I was just configuring Hibernate to ensure enhanced entities have a very plain interceptor, which the enhanced code relies on being present. I do not see where I am customizing the dirty checking. That will still be handled by $$_hibernate_getDirtyAttributes/$$_hibernate_hasDirtyAttributes will it not ? As you pointed out the enhanced code requires an interceptor to be present to work. There may be another way to configure Hibernate to provide one, but providing it via entity annotations is just another way of doing it. – user467257 Jun 19 '15 at 13:28
  • I don't understand your point. The interceptor does not need to handle any dirty checking, as far as I can see you can just set it to always return the new value. Then the enhanced dirty checking works properly. By configuring via annotations a simple dummy interceptor, the enhanced dirty checking works. I thought this was what you wanted. Otherwise this question is just a bug report: "The enhanced code requires an interceptor - either it shouldn't or Hibernate should supply a dummy, without explicitly being told to via annotations". – user467257 Jun 19 '15 at 13:51
  • I'll test it and let you know. – Vlad Mihalcea Jun 19 '15 at 13:54