FileDocCategorySizeDatePackage
AbstractSaveEventListener.javaAPI DocHibernate 3.2.517039Thu Dec 07 14:53:42 GMT 2006org.hibernate.event.def

AbstractSaveEventListener

public abstract class AbstractSaveEventListener extends AbstractReassociateEventListener
A convenience bas class for listeners responding to save events.
author
Steve Ebersole.

Fields Summary
protected static final int
PERSISTENT
protected static final int
TRANSIENT
protected static final int
DETACHED
protected static final int
DELETED
private static final Log
log
Constructors Summary
Methods Summary
protected voidcascadeAfterSave(org.hibernate.event.EventSource source, org.hibernate.persister.entity.EntityPersister persister, java.lang.Object entity, java.lang.Object anything)
Handles to calls needed to perform post-save cascades.

param
source The session from which the event originated.
param
persister The entity's persister instance.
param
entity The entity beng saved.
param
anything Generally cascade-specific data


		// cascade-save to collections AFTER the collection owner was saved
		source.getPersistenceContext().incrementCascadeLevel();
		try {
			new Cascade( getCascadeAction(), Cascade.AFTER_INSERT_BEFORE_DELETE, source )
					.cascade( persister, entity, anything );
		}
		finally {
			source.getPersistenceContext().decrementCascadeLevel();
		}
	
protected voidcascadeBeforeSave(org.hibernate.event.EventSource source, org.hibernate.persister.entity.EntityPersister persister, java.lang.Object entity, java.lang.Object anything)
Handles the calls needed to perform pre-save cascades for the given entity.

param
source The session from whcih the save event originated.
param
persister The entity's persister instance.
param
entity The entity to be saved.
param
anything Generally cascade-specific data


		// cascade-save to many-to-one BEFORE the parent is saved
		source.getPersistenceContext().incrementCascadeLevel();
		try {
			new Cascade( getCascadeAction(), Cascade.BEFORE_INSERT_AFTER_DELETE, source )
					.cascade( persister, entity, anything );
		}
		finally {
			source.getPersistenceContext().decrementCascadeLevel();
		}
	
protected java.lang.BooleangetAssumedUnsaved()

		return null;
	
protected abstract org.hibernate.engine.CascadingActiongetCascadeAction()

protected intgetEntityState(java.lang.Object entity, java.lang.String entityName, org.hibernate.engine.EntityEntry entry, org.hibernate.engine.SessionImplementor source)
Determine whether the entity is persistent, detached, or transient

param
entity The entity to check
param
entityName The name of the entity
param
entry The entity's entry in the persistence context
param
source The originating session.
return
The state.


		if ( entry != null ) { // the object is persistent

			//the entity is associated with the session, so check its status
			if ( entry.getStatus() != Status.DELETED ) {
				// do nothing for persistent instances
				if ( log.isTraceEnabled() ) {
					log.trace(
							"persistent instance of: " +
									getLoggableName( entityName, entity )
					);
				}
				return PERSISTENT;
			}
			else {
				//ie. e.status==DELETED
				if ( log.isTraceEnabled() ) {
					log.trace(
							"deleted instance of: " +
									getLoggableName( entityName, entity )
					);
				}
				return DELETED;
			}

		}
		else { // the object is transient or detached

			//the entity is not associated with the session, so
			//try interceptor and unsaved-value

			if ( ForeignKeys.isTransient( entityName, entity, getAssumedUnsaved(), source ) ) {
				if ( log.isTraceEnabled() ) {
					log.trace(
							"transient instance of: " +
									getLoggableName( entityName, entity )
					);
				}
				return TRANSIENT;
			}
			else {
				if ( log.isTraceEnabled() ) {
					log.trace(
							"detached instance of: " +
									getLoggableName( entityName, entity )
					);
				}
				return DETACHED;
			}

		}
	
protected java.lang.StringgetLoggableName(java.lang.String entityName, java.lang.Object entity)

		return entityName == null ? entity.getClass().getName() : entityName;
	
protected java.util.MapgetMergeMap(java.lang.Object anything)

		return null;
	
protected booleaninvokeSaveLifecycle(java.lang.Object entity, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.event.EventSource source)

		// Sub-insertions should occur before containing insertion so
		// Try to do the callback now
		if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
			log.debug( "calling onSave()" );
			if ( ( ( Lifecycle ) entity ).onSave( source ) ) {
				log.debug( "insertion vetoed by onSave()" );
				return true;
			}
		}
		return false;
	
protected booleanisVersionIncrementDisabled()
After the save, will te version number be incremented if the instance is modified?

return
True if the version will be incremented on an entity change after save; false otherwise.

		return false;
	
private voidmarkInterceptorDirty(java.lang.Object entity, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.event.EventSource source)

		if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
			FieldInterceptor interceptor = FieldInterceptionHelper.injectFieldInterceptor(
					entity,
					persister.getEntityName(),
					null,
					source
			);
			interceptor.dirty();
		}
	
protected java.io.SerializableperformSave(java.lang.Object entity, java.io.Serializable id, org.hibernate.persister.entity.EntityPersister persister, boolean useIdentityColumn, java.lang.Object anything, org.hibernate.event.EventSource source, boolean requiresImmediateIdAccess)
Ppepares the save call by checking the session caches for a pre-existing entity and performing any lifecycle callbacks.

param
entity The entity to be saved.
param
id The id by which to save the entity.
param
persister The entity's persister instance.
param
useIdentityColumn Is an identity column being used?
param
anything Generally cascade-specific information.
param
source The session from which the event originated.
param
requiresImmediateIdAccess does the event context require access to the identifier immediately after execution of this method (if not, post-insert style id generators may be postponed if we are outside a transaction).
return
The id used to save the entity; may be null depending on the type of id generator used and the requiresImmediateIdAccess value


		if ( log.isTraceEnabled() ) {
			log.trace(
					"saving " +
							MessageHelper.infoString( persister, id, source.getFactory() )
			);
		}

		EntityKey key;
		if ( !useIdentityColumn ) {
			key = new EntityKey( id, persister, source.getEntityMode() );
			Object old = source.getPersistenceContext().getEntity( key );
			if ( old != null ) {
				if ( source.getPersistenceContext().getEntry( old ).getStatus() == Status.DELETED ) {
					source.forceFlush( source.getPersistenceContext().getEntry( old ) );
				}
				else {
					throw new NonUniqueObjectException( id, persister.getEntityName() );
				}
			}
			persister.setIdentifier( entity, id, source.getEntityMode() );
		}
		else {
			key = null;
		}

		if ( invokeSaveLifecycle( entity, persister, source ) ) {
			return id; //EARLY EXIT
		}

		return performSaveOrReplicate(
				entity,
				key,
				persister,
				useIdentityColumn,
				anything,
				source,
				requiresImmediateIdAccess
		);
	
protected java.io.SerializableperformSaveOrReplicate(java.lang.Object entity, org.hibernate.engine.EntityKey key, org.hibernate.persister.entity.EntityPersister persister, boolean useIdentityColumn, java.lang.Object anything, org.hibernate.event.EventSource source, boolean requiresImmediateIdAccess)
Performs all the actual work needed to save an entity (well to get the save moved to the execution queue).

param
entity The entity to be saved
param
key The id to be used for saving the entity (or null, in the case of identity columns)
param
persister The entity's persister instance.
param
useIdentityColumn Should an identity column be used for id generation?
param
anything Generally cascade-specific information.
param
source The session which is the source of the current event.
param
requiresImmediateIdAccess Is access to the identifier required immediately after the completion of the save? persist(), for example, does not require this...
return
The id used to save the entity; may be null depending on the type of id generator used and the requiresImmediateIdAccess value


		validate( entity, persister, source );

		Serializable id = key == null ? null : key.getIdentifier();

		boolean inTxn = source.getJDBCContext().isTransactionInProgress();
		boolean shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;

		if ( useIdentityColumn && !shouldDelayIdentityInserts ) {
			log.trace( "executing insertions" );
			source.getActionQueue().executeInserts();
		}

		// 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()?
		source.getPersistenceContext().addEntry(
				entity,
				Status.SAVING,
				null,
				null,
				id,
				null,
				LockMode.WRITE,
				useIdentityColumn,
				persister,
				false,
				false
		);

		cascadeBeforeSave( source, persister, entity, anything );

		Object[] values = persister.getPropertyValuesToInsert( entity, getMergeMap( anything ), source );
		Type[] types = persister.getPropertyTypes();

		boolean substitute = substituteValuesIfNecessary( entity, id, values, persister, source );

		if ( persister.hasCollections() ) {
			substitute = substitute || visitCollectionsBeforeSave( entity, id, values, types, source );
		}

		if ( substitute ) {
			persister.setPropertyValues( entity, values, source.getEntityMode() );
		}

		TypeFactory.deepCopy(
				values,
				types,
				persister.getPropertyUpdateability(),
				values,
				source
		);

		new ForeignKeys.Nullifier( entity, false, useIdentityColumn, source )
				.nullifyTransientReferences( values, types );
		new Nullability( source ).checkNullability( values, persister, false );

		if ( useIdentityColumn ) {
			EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
					values, entity, persister, source, shouldDelayIdentityInserts
			);
			if ( !shouldDelayIdentityInserts ) {
				log.debug( "executing identity-insert immediately" );
				source.getActionQueue().execute( insert );
				id = insert.getGeneratedId();
				//now done in EntityIdentityInsertAction
				//persister.setIdentifier( entity, id, source.getEntityMode() );
				key = new EntityKey( id, persister, source.getEntityMode() );
				source.getPersistenceContext().checkUniqueness( key, entity );
				//source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
			}
			else {
				log.debug( "delaying identity-insert due to no transaction in progress" );
				source.getActionQueue().addAction( insert );
				key = insert.getDelayedEntityKey();
			}
		}

		Object version = Versioning.getVersion( values, persister );
		source.getPersistenceContext().addEntity(
				entity,
				Status.MANAGED,
				values,
				key,
				version,
				LockMode.WRITE,
				useIdentityColumn,
				persister,
				isVersionIncrementDisabled(),
				false
		);
		//source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );

		if ( !useIdentityColumn ) {
			source.getActionQueue().addAction(
					new EntityInsertAction( id, values, entity, version, persister, source )
			);
		}

		cascadeAfterSave( source, persister, entity, anything );

		markInterceptorDirty( entity, persister, source );

		return id;
	
protected java.io.SerializablesaveWithGeneratedId(java.lang.Object entity, java.lang.String entityName, java.lang.Object anything, org.hibernate.event.EventSource source, boolean requiresImmediateIdAccess)
Prepares the save call using a newly generated id.

param
entity The entity to be saved
param
entityName The entity-name for the entity to be saved
param
anything Generally cascade-specific information.
param
source The session which is the source of this save event.
param
requiresImmediateIdAccess does the event context require access to the identifier immediately after execution of this method (if not, post-insert style id generators may be postponed if we are outside a transaction).
return
The id used to save the entity; may be null depending on the type of id generator used and the requiresImmediateIdAccess value

		EntityPersister persister = source.getEntityPersister( entityName, entity );
		Serializable generatedId = persister.getIdentifierGenerator().generate( source, entity );
		if ( generatedId == null ) {
			throw new IdentifierGenerationException( "null id generated for:" + entity.getClass() );
		}
		else if ( generatedId == IdentifierGeneratorFactory.SHORT_CIRCUIT_INDICATOR ) {
			return source.getIdentifier( entity );
		}
		else if ( generatedId == IdentifierGeneratorFactory.POST_INSERT_INDICATOR ) {
			return performSave( entity, null, persister, true, anything, source, requiresImmediateIdAccess );
		}
		else {

			if ( log.isDebugEnabled() ) {
				log.debug(
						"generated identifier: " +
								persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ) +
								", using strategy: " +
								persister.getIdentifierGenerator().getClass().getName()
						//TODO: define toString()s for generators
				);
			}

			return performSave( entity, generatedId, persister, false, anything, source, true );
		}
	
protected java.io.SerializablesaveWithRequestedId(java.lang.Object entity, java.io.Serializable requestedId, java.lang.String entityName, java.lang.Object anything, org.hibernate.event.EventSource source)
Prepares the save call using the given requested id.

param
entity The entity to be saved.
param
requestedId The id to which to associate the entity.
param
entityName The name of the entity being saved.
param
anything Generally cascade-specific information.
param
source The session which is the source of this save event.
return
The id used to save the entity.


	                                                            	 
	  
			 
			 
			 
			 
			  
		return performSave(
				entity,
				requestedId,
				source.getEntityPersister( entityName, entity ),
				false,
				anything,
				source,
				true
		);
	
protected booleansubstituteValuesIfNecessary(java.lang.Object entity, java.io.Serializable id, java.lang.Object[] values, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.engine.SessionImplementor source)
Perform any property value substitution that is necessary (interceptor callback, version initialization...)

param
entity The entity
param
id The entity identifier
param
values The snapshot entity state
param
persister The entity persister
param
source The originating session
return
True if the snapshot state changed such that reinjection of the values into the entity is required.

		boolean substitute = source.getInterceptor().onSave(
				entity,
				id,
				values,
				persister.getPropertyNames(),
				persister.getPropertyTypes()
		);

		//keep the existing version number in the case of replicate!
		if ( persister.isVersioned() ) {
			substitute = Versioning.seedVersion(
					values,
					persister.getVersionProperty(),
					persister.getVersionType(),
					source
			) || substitute;
		}
		return substitute;
	
protected voidvalidate(java.lang.Object entity, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.event.EventSource source)

		if ( persister.implementsValidatable( source.getEntityMode() ) ) {
			( ( Validatable ) entity ).validate();
		}
	
protected booleanvisitCollectionsBeforeSave(java.lang.Object entity, java.io.Serializable id, java.lang.Object[] values, org.hibernate.type.Type[] types, org.hibernate.event.EventSource source)

		WrapVisitor visitor = new WrapVisitor( source );
		// substitutes into values by side-effect
		visitor.processEntityPropertyValues( values, types );
		return visitor.isSubstitutionRequired();