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?
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.
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:
- Register an entity listener by adding
@EntityListeners(YourListener.class)to the entities - Add implementations for all
@Pre/@Post(eg@PrePersistetc) methods to yourYourListener.classwhere you check if the entity is an instance ofPersistentAttributeInterceptable, and if it is just call$$_hibernate_setInterceptoron it with a customPersistentAttributeInterceptorthat 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.
来源:https://stackoverflow.com/questions/25702382/hibernate-4-bytecode-enhancement-not-working-for-dirty-checking-optimization