org.hibernate.event.EventSource Java Examples

The following examples show how to use org.hibernate.event.EventSource. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: EntityUpdateAction.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
private void postUpdate() {
	PostUpdateEventListener[] postListeners = getSession().getListeners()
			.getPostUpdateEventListeners();
	if (postListeners.length>0) {
		PostUpdateEvent postEvent = new PostUpdateEvent( 
				getInstance(), 
				getId(), 
				state, 
				previousState, 
				getPersister(),
				(EventSource) getSession() 
			);
		for ( int i = 0; i < postListeners.length; i++ ) {
			postListeners[i].onPostUpdate(postEvent);
		}
	}
}
 
Example #2
Source File: DefaultMergeEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
/** 
 * Perform any cascades needed as part of this copy event.
 *
 * @param source The merge event being processed.
 * @param persister The persister of the entity being copied.
 * @param entity The entity being copied.
 * @param copyCache A cache of already copied instance.
 */
protected void cascadeOnMerge(
	final EventSource source,
	final EntityPersister persister,
	final Object entity,
	final Map copyCache
) {
	source.getPersistenceContext().incrementCascadeLevel();
	try {
		new Cascade( getCascadeAction(), Cascade.BEFORE_MERGE, source )
				.cascade(persister, entity, copyCache);
	}
	finally {
		source.getPersistenceContext().decrementCascadeLevel();
	}
}
 
Example #3
Source File: AbstractSaveEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
/**
 * 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
 */
protected void cascadeAfterSave(
		EventSource source,
		EntityPersister persister,
		Object entity,
		Object anything) {

	// 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();
	}
}
 
Example #4
Source File: AbstractFlushingEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
/**
 * Execute all SQL and second-level cache updates, in a
 * special order so that foreign-key constraints cannot
 * be violated:
 * <ol>
 * <li> Inserts, in the order they were performed
 * <li> Updates
 * <li> Deletion of collection elements
 * <li> Insertion of collection elements
 * <li> Deletes, in the order they were performed
 * </ol>
 */
protected void performExecutions(EventSource session) throws HibernateException {

	log.trace("executing flush");

	try {
		session.getJDBCContext().getConnectionManager().flushBeginning();
		// we need to lock the collection caches before
		// executing entity inserts/updates in order to
		// account for bidi associations
		session.getActionQueue().prepareActions();
		session.getActionQueue().executeActions();
	}
	catch (HibernateException he) {
		log.error("Could not synchronize database state with session", he);
		throw he;
	}
	finally {
		session.getJDBCContext().getConnectionManager().flushEnding();
	}
}
 
Example #5
Source File: DefaultMergeEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
private boolean isVersionChanged(Object entity, EventSource source, EntityPersister persister, Object target) {
	if ( ! persister.isVersioned() ) {
		return false;
	}
	// for merging of versioned entities, we consider the version having
	// been changed only when:
	// 1) the two version values are different;
	//      *AND*
	// 2) The target actually represents database state!
	//
	// This second condition is a special case which allows
	// an entity to be merged during the same transaction
	// (though during a seperate operation) in which it was
	// originally persisted/saved
	boolean changed = ! persister.getVersionType().isSame(
			persister.getVersion( target, source.getEntityMode() ),
			persister.getVersion( entity, source.getEntityMode() ),
			source.getEntityMode()
	);

	// TODO : perhaps we should additionally require that the incoming entity
	// version be equivalent to the defined unsaved-value?
	return changed && existsInDatabase( target, source, persister );
}
 
Example #6
Source File: DefaultMergeEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
private boolean existsInDatabase(Object entity, EventSource source, EntityPersister persister) {
	EntityEntry entry = source.getPersistenceContext().getEntry( entity );
	if ( entry == null ) {
		Serializable id = persister.getIdentifier( entity, source.getEntityMode() );
		if ( id != null ) {
			EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
			Object managedEntity = source.getPersistenceContext().getEntity( key );
			entry = source.getPersistenceContext().getEntry( managedEntity );
		}
	}

	if ( entry == null ) {
		// perhaps this should be an exception since it is only ever used
		// in the above method?
		return false;
	}
	else {
		return entry.isExistsInDatabase();
	}
}
 
Example #7
Source File: IteratorImpl.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
public IteratorImpl(
        ResultSet rs,
        PreparedStatement ps,
        EventSource sess,
        Type[] types,
        String[][] columnNames,
        HolderInstantiator holderInstantiator)
throws HibernateException, SQLException {

	this.rs=rs;
	this.ps=ps;
	this.session = sess;
	this.types = types;
	this.names = columnNames;
	this.holderInstantiator = holderInstantiator;

	single = types.length==1;

	postNext();
}
 
Example #8
Source File: DefaultSaveOrUpdateEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
/**
 * The given save-update event named a transient entity.
 * <p/>
 * Here, we will perform the save processing.
 *
 * @param event The save event to be handled.
 *
 * @return The entity's identifier after saving.
 */
protected Serializable entityIsTransient(SaveOrUpdateEvent event) {

	log.trace( "saving transient instance" );

	final EventSource source = event.getSession();

	EntityEntry entityEntry = event.getEntry();
	if ( entityEntry != null ) {
		if ( entityEntry.getStatus() == Status.DELETED ) {
			source.forceFlush( entityEntry );
		}
		else {
			throw new AssertionFailure( "entity was persistent" );
		}
	}

	Serializable id = saveWithGeneratedOrRequestedId( event );

	source.getPersistenceContext().reassociateProxy( event.getObject(), id );

	return id;
}
 
Example #9
Source File: DefaultFlushEntityEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
private boolean wrapCollections(
		EventSource session,
		EntityPersister persister,
		Type[] types,
		Object[] values
) {
	if ( persister.hasCollections() ) {

		// wrap up any new collections directly referenced by the object
		// or its components

		// NOTE: we need to do the wrap here even if its not "dirty",
		// because collections need wrapping but changes to _them_
		// don't dirty the container. Also, for versioned data, we
		// need to wrap before calling searchForDirtyCollections

		WrapVisitor visitor = new WrapVisitor(session);
		// substitutes into values by side-effect
		visitor.processEntityPropertyValues(values, types);
		return visitor.isSubstitutionRequired();
	}
	else {
		return false;
	}
}
 
Example #10
Source File: EntityDeleteAction.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
private void postCommitDelete() {
	PostDeleteEventListener[] postListeners = getSession().getListeners()
			.getPostCommitDeleteEventListeners();
	if (postListeners.length>0) {
		PostDeleteEvent postEvent = new PostDeleteEvent(
				getInstance(),
				getId(),
				state,
				getPersister(),
				(EventSource) getSession()
		);
		for ( int i = 0; i < postListeners.length; i++ ) {
			postListeners[i].onPostDelete(postEvent);
		}
	}
}
 
Example #11
Source File: DefaultEvictEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
protected void doEvict(
	final Object object, 
	final EntityKey key, 
	final EntityPersister persister,
	final EventSource session) throws HibernateException {

	if ( log.isTraceEnabled() ) {
		log.trace( "evicting " + MessageHelper.infoString(persister) );
	}

	// remove all collections for the entity from the session-level cache
	if ( persister.hasCollections() ) {
		new EvictVisitor( session ).process( object, persister );
	}

	new Cascade( CascadingAction.EVICT, Cascade.AFTER_EVICT, session )
			.cascade( persister, object );
}
 
Example #12
Source File: DefaultDeleteEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
/**
 * We encountered a delete request on a transient instance.
 * <p/>
 * This is a deviation from historical Hibernate (pre-3.2) behavior to
 * align with the JPA spec, which states that transient entities can be
 * passed to remove operation in which case cascades still need to be
 * performed.
 *
 * @param session The session which is the source of the event
 * @param entity The entity being delete processed
 * @param cascadeDeleteEnabled Is cascading of deletes enabled
 * @param persister The entity persister
 * @param transientEntities A cache of already visited transient entities
 * (to avoid infinite recursion).
 */
protected void deleteTransientEntity(
		EventSource session,
		Object entity,
		boolean cascadeDeleteEnabled,
		EntityPersister persister,
		Set transientEntities) {
	log.info( "handling transient entity in delete processing" );
	if ( transientEntities.contains( entity ) ) {
		log.trace( "already handled transient entity; skipping" );
		return;
	}
	transientEntities.add( entity );
	cascadeBeforeDelete( session, persister, entity, null, transientEntities );
	cascadeAfterDelete( session, persister, entity, transientEntities );
}
 
Example #13
Source File: DefaultDeleteEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
protected void cascadeBeforeDelete(
		EventSource session,
		EntityPersister persister,
		Object entity,
		EntityEntry entityEntry,
		Set transientEntities) throws HibernateException {

	CacheMode cacheMode = session.getCacheMode();
	session.setCacheMode( CacheMode.GET );
	session.getPersistenceContext().incrementCascadeLevel();
	try {
		// cascade-delete to collections BEFORE the collection owner is deleted
		new Cascade( CascadingAction.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session )
				.cascade( persister, entity, transientEntities );
	}
	finally {
		session.getPersistenceContext().decrementCascadeLevel();
		session.setCacheMode( cacheMode );
	}
}
 
Example #14
Source File: DefaultDeleteEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
protected void cascadeAfterDelete(
		EventSource session,
		EntityPersister persister,
		Object entity,
		Set transientEntities) throws HibernateException {

	CacheMode cacheMode = session.getCacheMode();
	session.setCacheMode( CacheMode.GET );
	session.getPersistenceContext().incrementCascadeLevel();
	try {
		// cascade-delete to many-to-one AFTER the parent was deleted
		new Cascade( CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session )
				.cascade( persister, entity, transientEntities );
	}
	finally {
		session.getPersistenceContext().decrementCascadeLevel();
		session.setCacheMode( cacheMode );
	}
}
 
Example #15
Source File: AbstractSaveEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
/**
 * 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
 */
protected void cascadeBeforeSave(
		EventSource source,
		EntityPersister persister,
		Object entity,
		Object anything) {

	// 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();
	}
}
 
Example #16
Source File: EntityUpdateAction.java    From cacheonix-core with GNU Lesser General Public License v2.1 6 votes vote down vote up
private void postCommitUpdate() {
	PostUpdateEventListener[] postListeners = getSession().getListeners()
			.getPostCommitUpdateEventListeners();
	if (postListeners.length>0) {
		PostUpdateEvent postEvent = new PostUpdateEvent( 
				getInstance(), 
				getId(), 
				state, 
				previousState, 
				getPersister(),
				(EventSource) getSession()
			);
		for ( int i = 0; i < postListeners.length; i++ ) {
			postListeners[i].onPostUpdate(postEvent);
		}
	}
}
 
Example #17
Source File: AbstractFlushingEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
/**
 * 1. detect any dirty entities
 * 2. schedule any entity updates
 * 3. search out any reachable collections
 */
private void flushEntities(FlushEvent event) throws HibernateException {

	log.trace("Flushing entities and processing referenced collections");

	// Among other things, updateReachables() will recursively load all
	// collections that are moving roles. This might cause entities to
	// be loaded.

	// So this needs to be safe from concurrent modification problems.
	// It is safe because of how IdentityMap implements entrySet()

	final EventSource source = event.getSession();
	
	final Map.Entry[] list = IdentityMap.concurrentEntries( source.getPersistenceContext().getEntityEntries() );
	final int size = list.length;
	for ( int i = 0; i < size; i++ ) {

		// Update the status of the object and if necessary, schedule an update

		Map.Entry me = list[i];
		EntityEntry entry = (EntityEntry) me.getValue();
		Status status = entry.getStatus();

		if ( status != Status.LOADING && status != Status.GONE ) {
			FlushEntityEvent entityEvent = new FlushEntityEvent( source, me.getKey(), entry );
			FlushEntityEventListener[] listeners = source.getListeners().getFlushEntityEventListeners();
			for ( int j = 0; j < listeners.length; j++ ) {
				listeners[j].onFlushEntity(entityEvent);
			}
		}
	}

	source.getActionQueue().sortActions();
}
 
Example #18
Source File: ReattachVisitor.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
/**
 * Schedules a collection for deletion.
 *
 * @param role The persister representing the collection to be removed.
 * @param collectionKey The collection key (differs from owner-id in the case of property-refs).
 * @param source The session from which the request originated.
 * @throws HibernateException
 */
void removeCollection(CollectionPersister role, Serializable collectionKey, EventSource source) throws HibernateException {
	if ( log.isTraceEnabled() ) {
		log.trace(
				"collection dereferenced while transient " +
				MessageHelper.collectionInfoString( role, ownerIdentifier, source.getFactory() )
		);
	}
	source.getActionQueue().addAction( new CollectionRemoveAction( null, role, collectionKey, false, source ) );
}
 
Example #19
Source File: OnReplicateVisitor.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
Object processCollection(Object collection, CollectionType type)
		throws HibernateException {

	if ( collection == CollectionType.UNFETCHED_COLLECTION ) {
		return null;
	}

	EventSource session = getSession();
	CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() );

	if ( isUpdate ) {
		removeCollection( persister, extractCollectionKeyFromOwner( persister ), session );
	}
	if ( collection != null && ( collection instanceof PersistentCollection ) ) {
		PersistentCollection wrapper = ( PersistentCollection ) collection;
		wrapper.setCurrentSession( session );
		if ( wrapper.wasInitialized() ) {
			session.getPersistenceContext().addNewCollection( persister, wrapper );
		}
		else {
			reattachCollection( wrapper, type );
		}
	}
	else {
		// otherwise a null or brand new collection
		// this will also (inefficiently) handle arrays, which
		// have no snapshot, so we can't do any better
		//processArrayOrNewCollection(collection, type);
	}

	return null;

}
 
Example #20
Source File: CacheEntry.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
public Object[] assemble(
		final Object instance, 
		final Serializable id, 
		final EntityPersister persister, 
		final Interceptor interceptor, 
		final EventSource session) 
throws HibernateException {

	if ( !persister.getEntityName().equals(subclass) ) {
		throw new AssertionFailure("Tried to assemble a different subclass instance");
	}

	return assemble(disassembledState, instance, id, persister, interceptor, session);

}
 
Example #21
Source File: DefaultDeleteEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
private Object[] createDeletedState(EntityPersister persister, Object[] currentState, EventSource session) {
		Type[] propTypes = persister.getPropertyTypes();
		final Object[] deletedState = new Object[propTypes.length];
//		TypeFactory.deepCopy( currentState, propTypes, persister.getPropertyUpdateability(), deletedState, session );
		boolean[] copyability = new boolean[propTypes.length];
		java.util.Arrays.fill( copyability, true );
		TypeFactory.deepCopy( currentState, propTypes, copyability, deletedState, session );
		return deletedState;
	}
 
Example #22
Source File: AbstractFlushingEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
private void cascadeOnFlush(EventSource session, EntityPersister persister, Object object, Object anything) 
throws HibernateException {
	session.getPersistenceContext().incrementCascadeLevel();
	try {
		new Cascade( getCascadingAction(), Cascade.BEFORE_FLUSH, session )
		.cascade( persister, object, anything );
	}
	finally {
		session.getPersistenceContext().decrementCascadeLevel();
	}
}
 
Example #23
Source File: DefaultDeleteEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) {
	if ( persister.implementsLifecycle( session.getEntityMode() ) ) {
		log.debug( "calling onDelete()" );
		if ( ( ( Lifecycle ) entity ).onDelete( session ) ) {
			log.debug( "deletion vetoed by onDelete()" );
			return true;
		}
	}
	return false;
}
 
Example #24
Source File: DefaultReplicateEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
private void cascadeAfterReplicate(
		Object entity,
		EntityPersister persister,
		ReplicationMode replicationMode,
		EventSource source) {
	source.getPersistenceContext().incrementCascadeLevel();
	try {
		new Cascade( CascadingAction.REPLICATE, Cascade.AFTER_UPDATE, source )
				.cascade( persister, entity, replicationMode );
	}
	finally {
		source.getPersistenceContext().decrementCascadeLevel();
	}
}
 
Example #25
Source File: DefaultSaveOrUpdateEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
/**
 * Handles the calls needed to perform cascades as part of an update request
 * for the given entity.
 *
 * @param event The event currently being processed.
 * @param persister The defined persister for the entity being updated.
 * @param entity The entity being updated.
 */
private void cascadeOnUpdate(SaveOrUpdateEvent event, EntityPersister persister, Object entity) {
	EventSource source = event.getSession();
	source.getPersistenceContext().incrementCascadeLevel();
	try {
		new Cascade( CascadingAction.SAVE_UPDATE, Cascade.AFTER_UPDATE, source )
				.cascade( persister, entity );
	}
	finally {
		source.getPersistenceContext().decrementCascadeLevel();
	}
}
 
Example #26
Source File: DefaultSaveOrUpdateEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
protected boolean invokeUpdateLifecycle(Object entity, EntityPersister persister, EventSource source) {
	if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
		log.debug( "calling onUpdate()" );
		if ( ( ( Lifecycle ) entity ).onUpdate( source ) ) {
			log.debug( "update vetoed by onUpdate()" );
			return true;
		}
	}
	return false;
}
 
Example #27
Source File: DefaultReplicateEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
private void performReplication(
		Object entity,
		Serializable id,
		Object version,
		EntityPersister persister,
		ReplicationMode replicationMode,
		EventSource source) throws HibernateException {

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

	new OnReplicateVisitor( source, id, entity, true ).process( entity, persister );

	source.getPersistenceContext().addEntity(
			entity,
			Status.MANAGED,
			null,
			new EntityKey( id, persister, source.getEntityMode() ),
			version,
			LockMode.NONE,
			true,
			persister,
			true,
			false
	);

	cascadeAfterReplicate( entity, persister, replicationMode, source );
}
 
Example #28
Source File: AbstractSaveEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
/**
 * 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
 */
protected Serializable saveWithGeneratedId(
		Object entity,
		String entityName,
		Object anything,
		EventSource source,
		boolean requiresImmediateIdAccess) {
	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 );
	}
}
 
Example #29
Source File: DefaultFlushEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
/** Handle the given flush event.
 *
 * @param event The flush event to be handled.
 * @throws HibernateException
 */
public void onFlush(FlushEvent event) throws HibernateException {
	final EventSource source = event.getSession();
	if ( source.getPersistenceContext().hasNonReadOnlyEntities() ) {
		
		flushEverythingToExecutions(event);
		performExecutions(source);
		postFlush(source);
	
		if ( source.getFactory().getStatistics().isStatisticsEnabled() ) {
			source.getFactory().getStatisticsImplementor().flush();
		}
		
	}
}
 
Example #30
Source File: DefaultLockEventListener.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
private void cascadeOnLock(LockEvent event, EntityPersister persister, Object entity) {
	EventSource source = event.getSession();
	source.getPersistenceContext().incrementCascadeLevel();
	try {
		new Cascade(CascadingAction.LOCK, Cascade.AFTER_LOCK, source)
				.cascade( persister, entity, event.getLockMode() );
	}
	finally {
		source.getPersistenceContext().decrementCascadeLevel();
	}
}