📄 defaultsaveorupdateeventlistener.cs
字号:
using System;
using log4net;
using NHibernate.Classic;
using NHibernate.Engine;
using NHibernate.Impl;
using NHibernate.Persister.Entity;
using NHibernate.Proxy;
namespace NHibernate.Event.Default
{
/// <summary>
/// Defines the default listener used by Hibernate for handling save-update events.
/// </summary>
[Serializable]
public class DefaultSaveOrUpdateEventListener : AbstractSaveEventListener, ISaveOrUpdateEventListener
{
private static readonly ILog log = LogManager.GetLogger(typeof(DefaultSaveOrUpdateEventListener));
protected override CascadingAction CascadeAction
{
get { return CascadingAction.SaveUpdate; }
}
public virtual void OnSaveOrUpdate(SaveOrUpdateEvent @event)
{
ISessionImplementor source = @event.Session;
object obj = @event.Entity;
object requestedId = @event.RequestedId;
if (requestedId != null)
{
//assign the requested id to the proxy, *before*
//reassociating the proxy
if (obj is INHibernateProxy)
{
((INHibernateProxy)obj).HibernateLazyInitializer.Identifier = requestedId;
}
}
if (ReassociateIfUninitializedProxy(obj, source))
{
log.Debug("reassociated uninitialized proxy");
// an uninitialized proxy, noop, don't even need to
// return an id, since it is never a save()
}
else
{
//initialize properties of the event:
object entity = source.PersistenceContext.UnproxyAndReassociate(obj);
@event.Entity = entity;
@event.Entry = source.PersistenceContext.GetEntry(entity);
//return the id in the event object
@event.ResultId = PerformSaveOrUpdate(@event);
}
}
protected virtual bool ReassociateIfUninitializedProxy(object obj, ISessionImplementor source)
{
return source.PersistenceContext.ReassociateIfUninitializedProxy(obj);
}
protected virtual object PerformSaveOrUpdate(SaveOrUpdateEvent @event)
{
EntityState entityState = GetEntityState(@event.Entity, @event.EntityName, @event.Entry, @event.Session);
switch (entityState)
{
case EntityState.Detached:
EntityIsDetached(@event);
return null;
case EntityState.Persistent:
return EntityIsPersistent(@event);
default: //TRANSIENT or DELETED
return EntityIsTransient(@event);
}
}
protected virtual object EntityIsPersistent(SaveOrUpdateEvent @event)
{
log.Debug("ignoring persistent instance");
EntityEntry entityEntry = @event.Entry;
if (entityEntry == null)
{
throw new AssertionFailure("entity was transient or detached");
}
else
{
if (entityEntry.Status == Status.Deleted)
{
throw new AssertionFailure("entity was deleted");
}
ISessionFactoryImplementor factory = @event.Session.Factory;
object requestedId = @event.RequestedId;
object savedId;
if (requestedId == null)
{
savedId = entityEntry.Id;
}
else
{
if (!entityEntry.Persister.IdentifierType.IsEqual(requestedId, entityEntry.Id, EntityMode.Poco))
{
throw new PersistentObjectException("object passed to save() was already persistent: " +
MessageHelper.InfoString(entityEntry.Persister, requestedId, factory));
}
savedId = requestedId;
}
if (log.IsDebugEnabled)
{
log.Debug("object already associated with session: " +
MessageHelper.InfoString(entityEntry.Persister, savedId, factory));
}
return savedId;
}
}
/// <summary>
/// The given save-update event named a transient entity.
/// Here, we will perform the save processing.
/// </summary>
/// <param name="event">The save event to be handled. </param>
/// <returns> The entity's identifier after saving. </returns>
protected virtual object EntityIsTransient(SaveOrUpdateEvent @event)
{
log.Debug("saving transient instance");
IEventSource source = @event.Session;
EntityEntry entityEntry = @event.Entry;
if (entityEntry != null)
{
if (entityEntry.Status == Status.Deleted)
{
source.ForceFlush(entityEntry);
}
else
{
throw new AssertionFailure("entity was persistent");
}
}
object id = SaveWithGeneratedOrRequestedId(@event);
source.PersistenceContext.ReassociateProxy(@event.Entity, id);
return id;
}
/// <summary>
/// Save the transient instance, assigning the right identifier
/// </summary>
/// <param name="event">The initiating event. </param>
/// <returns> The entity's identifier value after saving.</returns>
protected virtual object SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent @event)
{
return SaveWithGeneratedId(@event.Entity, @event.EntityName, null, @event.Session, true);
}
/// <summary>
/// The given save-update event named a detached entity.
/// Here, we will perform the update processing.
/// </summary>
/// <param name="event">The update event to be handled. </param>
protected virtual void EntityIsDetached(SaveOrUpdateEvent @event)
{
log.Debug("updating detached instance");
if (@event.Session.PersistenceContext.IsEntryFor(@event.Entity))
{
//TODO: assertion only, could be optimized away
throw new AssertionFailure("entity was persistent");
}
object entity = @event.Entity;
IEntityPersister persister = @event.Session.GetEntityPersister(entity);
@event.RequestedId = GetUpdateId(entity, persister, @event.RequestedId, @event.Session.EntityMode);
PerformUpdate(@event, entity, persister);
}
/// <summary> Determine the id to use for updating. </summary>
/// <param name="entity">The entity. </param>
/// <param name="persister">The entity persister </param>
/// <param name="requestedId">The requested identifier </param>
/// <param name="entityMode">The entity mode. </param>
/// <returns> The id. </returns>
protected virtual object GetUpdateId(object entity, IEntityPersister persister, object requestedId, EntityMode entityMode)
{
// use the id assigned to the instance
object id = persister.GetIdentifier(entity, entityMode);
if (id == null)
{
// assume this is a newly instantiated transient object
// which should be saved rather than updated
throw new TransientObjectException("The given object has a null identifier: " + persister.EntityName);
}
else
{
return id;
}
}
protected virtual void PerformUpdate(SaveOrUpdateEvent @event, object entity, IEntityPersister persister)
{
if (!persister.IsMutable)
{
log.Debug("immutable instance passed to doUpdate(), locking");
Reassociate(@event, entity, @event.RequestedId, persister);
}
else
{
if (log.IsDebugEnabled)
{
log.Debug("updating " + MessageHelper.InfoString(persister, @event.RequestedId, @event.Session.Factory));
}
IEventSource source = @event.Session;
EntityKey key = new EntityKey(@event.RequestedId, persister, source.EntityMode);
source.PersistenceContext.CheckUniqueness(key, entity);
if (InvokeUpdateLifecycle(entity, persister, source))
{
Reassociate(@event, @event.Entity, @event.RequestedId, persister);
return;
}
// this is a transient object with existing persistent state not loaded by the session
new OnUpdateVisitor(source, @event.RequestedId, entity).Process(entity, persister);
//TODO: put this stuff back in to read snapshot from
// the second-level cache (needs some extra work)
/*Object[] cachedState = null;
if ( persister.hasCache() ) {
CacheEntry entry = (CacheEntry) persister.getCache()
.get( event.getRequestedId(), source.getTimestamp() );
cachedState = entry==null ?
null :
entry.getState(); //TODO: half-assemble this stuff
}*/
source.PersistenceContext.AddEntity(entity, Status.Loaded, null, key,
persister.GetVersion(entity, source.EntityMode), LockMode.None, true, persister,
false, true);
//persister.AfterReassociate(entity, source); TODO H3.2 not ported
if (log.IsDebugEnabled)
{
log.Debug("updating " + MessageHelper.InfoString(persister, @event.RequestedId, source.Factory));
}
CascadeOnUpdate(@event, persister, entity);
}
}
protected virtual bool InvokeUpdateLifecycle(object entity, IEntityPersister persister, IEventSource source)
{
if (persister.ImplementsLifecycle(source.EntityMode))
{
log.Debug("calling onUpdate()");
if (((ILifecycle)entity).OnUpdate(source) == LifecycleVeto.Veto)
{
log.Debug("update vetoed by onUpdate()");
return true;
}
}
return false;
}
/// <summary>
/// Handles the calls needed to perform cascades as part of an update request
/// for the given entity.
/// </summary>
/// <param name="event">The event currently being processed. </param>
/// <param name="persister">The defined persister for the entity being updated. </param>
/// <param name="entity">The entity being updated. </param>
private void CascadeOnUpdate(SaveOrUpdateEvent @event, IEntityPersister persister, object entity)
{
IEventSource source = @event.Session;
source.PersistenceContext.IncrementCascadeLevel();
try
{
new Cascade(CascadingAction.SaveUpdate, CascadePoint.AfterUpdate, source).CascadeOn(persister, entity);
}
finally
{
source.PersistenceContext.DecrementCascadeLevel();
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -