Hibernate version: 3.2.5
Name and version of the database you are using: Oracle 10g
I'm seeing some behavior that I'm not sure is by design with db snapshots and flushing.
We've got a custom persister that attempts to keep track of individual changes to our entities through the use of an 'originalValues' map that gets updated via property changes on the entity itself. It uses that map to determine a db snapshot to determine what properties need to be updated.
The 2 methods on the persister are as follows
Code:
public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session) throws HibernateException {
HibernateDomainObject entity = (HibernateDomainObject) session.getEntityUsingInterceptor(new EntityKey(id, this, session.getEntityMode()));
Map originalValues = entity.getOriginalValues();
Type[] types = getPropertyTypes();
String[] propertyNames = getPropertyNames();
Object[] values = new Object[propertyNames.length];
boolean[] includeProperty = getPropertyUpdateability();
for (int i = 0; i < propertyNames.length; i++) {
if (includeProperty[i]) {
if (originalValues.containsKey(propertyNames[i])) {
values[i] = types[i].disassemble(originalValues.get(propertyNames[i]), session, null);
} else {
values[i] = types[i].disassemble(getPropertyValue(entity, propertyNames[i], session.getEntityMode()), session, null);
}
}
}
return values;
}
public boolean isSelectBeforeUpdateRequired() {
return true;
}
The isSelectBeforeUpdateRequired will force the snapshot to be used when checking for modified data and that all seems to work (been using it for years now)
However, we're looking at beefing up our Auditing in the interceptor and I noticed a bug on our side of things where I wasn't clearing our originalValues in the right spot. I was doing it in the postFlush on the interceptor but it seemed to me that we ended up processing the same records multiple times in the flush so I added it to the finally block. Our onFlushDirty looks like this:
Code:
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException {
try {
if (entity instanceof Auditable && !((HibernateDomainObject) entity).getOriginalValues().isEmpty()) {
for (int i = 0; i < propertyNames.length; i++) {
if (Auditable.AUDITABLE_AUDIT_INFO_PROPERTY.equals(propertyNames[i])) {
AuditInfo ai = ((Auditable) entity).getAuditInfo();
AuditInfo newAuditInfo = new AuditInfo(ai.getCreateDate(), ai.getCreateUser(), new Timestamp(System.currentTimeMillis()), getUserName());
//need to update the current state so that the changes will get persisted
currentState[i] = newAuditInfo;
((Auditable) entity).setAuditInfo(newAuditInfo);
return true;
}
}
}
return false;
}
finally {
((HibernateDomainObject) entity).markUnchanged();
}
}
An additional change I made in regards to the finally block was to also set the auditInfo directly in addition to returning the value in the currentState as I found I kept getting into the same block afterwards with the auditInfo being the only value changed as it seems to call the setter somewhere after the onFlushDirty call.
What I'm finding now is that onFlushDirty happens, my auditInfo is set, the changes I'm tracking get cleared but I am seeing the same entity come through again with no tracked changes, i.e. it gets to the return false line.
As far as I can tell it is because later on when the object is checked for dirtyness it uses a cached value of the db snapshot in which it thinks the object is still dirty and processes it again. The changes I just made to our side of things won't update the audit info again but it still will fire an update since it thinks it is dirty.
I guess my bottom line question is:
After the flush occurs, should the snapshot either be updated to reflect the current state of the entity or maybe even cleared so that the persister can determine if the object is truly dirty?