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