Yes, I have read the Interceptor chapter.
Here is what I have observed about interceptors.
You basically define an EntityInterceptor and attach it to the session.
In your interceptor, you can implement methods like
onSave()
onLoad() etc
(Look at the api docs for a full list of methods that u have to impl)
SO you can do what u want in these methods, perhaps logging, auditing etc.. One can think of several other things that can be done here.
The API docs say that u need to return true if your method changes anything in the bean or entity that is being intercepted. So make sure that u return true if u modify the entity.
However, I have noticed the following.
Say I were inserting/ creating a a new bean, the onsave() method is called, however, I have noticed that any changes that u do to the bean/entity are not applied to the bean directly when it is being inserted.
The following happens,
First a INSERT is done using the actual values on the bean/entity,
and then an UPDATE is done using the values again on the changed bean.
I am not sure why it is done like this, meaning a two step process.
WEll, I have run into problems because of this:
Here is the scenario.
Assume that u have an entity that relates to a table in the db.
Now, let us assime that there is a non null col which is Fkey.
Now say I create a new bean and then save it, and I make my interceptor
set an object for the non null col attribute on my entity, It fails to insert the bean as the two step process fails, because in the first step, we get a
NOT NULL SQL Error!
I have noticed the onsave method do the following:
private Serializable doSave(
final Object object,
Key key,
final ClassPersister persister,
final boolean replicate,
final boolean useIdentityColumn,
final Cascades.CascadingAction cascadeAction,
final Object anything)
throws HibernateException {
if ( persister.implementsValidatable() ) ( (Validatable) object ).validate();
Serializable id;
if (useIdentityColumn) {
id = null;
executeInserts();
}
else {
id = key.getIdentifier();
}
// Put a placeholder in entries, so we don't recurse back and try to save() the
// same object again. QUESTION: should this be done before onSave() is called?
// likewise, should it be done before onUpdate()?
addEntry(object, SAVING, null, id, null, LockMode.WRITE, useIdentityColumn, persister, false); //okay if id is null here
// cascade-save to many-to-one BEFORE the parent is saved
cascading++;
try {
Cascades.cascade(this, persister, object, cascadeAction, Cascades.CASCADE_BEFORE_INSERT_AFTER_DELETE, anything);
}
finally {
cascading--;
}
Object[] values = persister.getPropertyValues(object);
Type[] types = persister.getPropertyTypes();
boolean substitute = false;
if (!replicate) {
substitute = interceptor.onSave( object, id, values, persister.getPropertyNames(), types );
//keep the existing version number in the case of replicate!
if ( persister.isVersioned() ) {
substitute = Versioning.seedVersion(
values, persister.getVersionProperty(), persister.getVersionType()
) || substitute;
}
}
if ( persister.hasCollections() ) {
//TODO: make OnReplicateVisitor extend WrapVisitor
if (replicate) {
OnReplicateVisitor visitor = new OnReplicateVisitor(this, id);
visitor.processValues(values, types);
}
WrapVisitor visitor = new WrapVisitor(this);
// substitutes into values by side-effect
visitor.processValues(values, types);
substitute = substitute || visitor.isSubstitutionRequired();
}
if (substitute) persister.setPropertyValues(object, values);
TypeFactory.deepCopy(values, types, persister.getPropertyUpdateability(), values);
nullifyTransientReferences(values, types, useIdentityColumn, object);
checkNullability(values, persister, false);
if (useIdentityColumn) {
ScheduledIdentityInsertion insert = new ScheduledIdentityInsertion(values, object, persister, this);
insert.execute();
executions.add(insert);
id = insert.getGeneratedId();
persister.setIdentifier(object, id);
key = new Key(id, persister);
checkUniqueness(key, object);
}
Object version = Versioning.getVersion(values, persister);
addEntity(key, object);
addEntry(object, LOADED, values, id, version, LockMode.WRITE, useIdentityColumn, persister, replicate);
nonExists.remove(key);
if (!useIdentityColumn) {
insertions.add( new ScheduledInsertion( id, values, object, persister, this ) );
}
// cascade-save to collections AFTER the collection owner was saved
cascading++;
try {
Cascades.cascade(this, persister, object, cascadeAction, Cascades.CASCADE_AFTER_INSERT_BEFORE_DELETE, anything);
}
finally {
cascading--;
}
return id;
}
In the above method in the SessionImpl.java, we get the values and types
Object[] values = persister.getPropertyValues(object);
Type[] types = persister.getPropertyTypes();
before we call the interceptor onSave() method
substitute = interceptor.onSave( object, id, values, persister.getPropertyNames(), types );
well, say we modify the object in the onSave() method, and return true.
Later down the method..
we do the following
if (substitute) persister.setPropertyValues(object, values);
well, as I understand it, we copy the values array back on to the object, at which point in time the attribute that I have set on the object in the interceptor are nulled out!!!
Well, I am not sure if this a problem with the persister implementation because it only copies the new values in to the object!!
Please let me know if I have made any wrong assumptions about how interceptors are supposed to work. I did not want to file a bug yet, as I wanted to invoke a disussion on interceptors.
thanks,
kiran
|