/// <summary> /// Prepares the save call by checking the session caches for a pre-existing /// entity and performing any lifecycle callbacks. /// </summary> /// <param name="entity">The entity to be saved. </param> /// <param name="id">The id by which to save the entity. </param> /// <param name="persister">The entity's persister instance. </param> /// <param name="useIdentityColumn">Is an identity column being used? </param> /// <param name="anything">Generally cascade-specific information. </param> /// <param name="source">The session from which the event originated. </param> /// <param name="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). /// </param> /// <returns> /// The id used to save the entity; may be null depending on the /// type of id generator used and the requiresImmediateIdAccess value /// </returns> protected virtual object PerformSave(object entity, object id, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess) { if (log.IsDebugEnabled()) { log.Debug("saving {0}", MessageHelper.InfoString(persister, id, source.Factory)); } EntityKey key; if (!useIdentityColumn) { key = source.GenerateEntityKey(id, persister); object old = source.PersistenceContext.GetEntity(key); if (old != null) { if (source.PersistenceContext.GetEntry(old).Status == Status.Deleted) { source.ForceFlush(source.PersistenceContext.GetEntry(old)); } else { throw new NonUniqueObjectException(id, persister.EntityName); } } persister.SetIdentifier(entity, id); } else { key = null; } if (InvokeSaveLifecycle(entity, persister, source)) { return(id); //EARLY EXIT } return(PerformSaveOrReplicate(entity, key, persister, useIdentityColumn, anything, source, requiresImmediateIdAccess)); }
private async Task PerformReplicationAsync(object entity, object id, object version, IEntityPersister persister, ReplicationMode replicationMode, IEventSource source, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (log.IsDebugEnabled) { log.Debug("replicating changes to " + MessageHelper.InfoString(persister, id, source.Factory)); } await(new OnReplicateVisitor(source, id, entity, true).ProcessAsync(entity, persister, cancellationToken)).ConfigureAwait(false); source.PersistenceContext.AddEntity( entity, persister.IsMutable ? Status.Loaded : Status.ReadOnly, null, source.GenerateEntityKey(id, persister), version, LockMode.None, true, persister, true, false); await(CascadeAfterReplicateAsync(entity, persister, replicationMode, source, cancellationToken)).ConfigureAwait(false); }
/// <summary> /// Associates a given entity (either transient or associated with another session) to the given session. /// </summary> /// <param name="event">The event triggering the re-association </param> /// <param name="entity">The entity to be associated </param> /// <param name="id">The id of the entity. </param> /// <param name="persister">The entity's persister instance. </param> /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param> /// <returns> An EntityEntry representing the entity within this session. </returns> protected async Task <EntityEntry> ReassociateAsync(AbstractEvent @event, object entity, object id, IEntityPersister persister, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (log.IsDebugEnabled()) { log.Debug("Reassociating transient instance: {0}", MessageHelper.InfoString(persister, id, @event.Session.Factory)); } IEventSource source = @event.Session; EntityKey key = source.GenerateEntityKey(id, persister); source.PersistenceContext.CheckUniqueness(key, entity); //get a snapshot object[] values = persister.GetPropertyValues(entity); TypeHelper.DeepCopy(values, persister.PropertyTypes, persister.PropertyUpdateability, values, source); object version = Versioning.GetVersion(values, persister); EntityEntry newEntry = source.PersistenceContext.AddEntity( entity, persister.IsMutable ? Status.Loaded : Status.ReadOnly, values, key, version, LockMode.None, true, persister, false, true); await(new OnLockVisitor(source, id, entity).ProcessAsync(entity, persister, cancellationToken)).ConfigureAwait(false); persister.AfterReassociate(entity, source); return(newEntry); }
/// <summary> /// Performs all the actual work needed to save an entity (well to get the save moved to /// the execution queue). /// </summary> /// <param name="entity">The entity to be saved </param> /// <param name="key">The id to be used for saving the entity (or null, in the case of identity columns) </param> /// <param name="persister">The entity's persister instance. </param> /// <param name="useIdentityColumn">Should an identity column be used for id generation? </param> /// <param name="anything">Generally cascade-specific information. </param> /// <param name="source">The session which is the source of the current event. </param> /// <param name="requiresImmediateIdAccess"> /// Is access to the identifier required immediately /// after the completion of the save? persist(), for example, does not require this... /// </param> /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param> /// <returns> /// The id used to save the entity; may be null depending on the /// type of id generator used and the requiresImmediateIdAccess value /// </returns> protected virtual async Task <object> PerformSaveOrReplicateAsync(object entity, EntityKey key, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Validate(entity, persister, source); object id = key == null ? null : key.Identifier; // NH Different behavior (shouldDelayIdentityInserts=false anyway) //bool inTxn = source.ConnectionManager.IsInActiveTransaction; //bool shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess; bool shouldDelayIdentityInserts = false; // 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.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false, false); await(CascadeBeforeSaveAsync(source, persister, entity, anything, cancellationToken)).ConfigureAwait(false); // NH-962: This was originally done before many-to-one cascades. if (useIdentityColumn && !shouldDelayIdentityInserts) { log.Debug("executing insertions"); await(source.ActionQueue.ExecuteInsertsAsync(cancellationToken)).ConfigureAwait(false); } object[] values = persister.GetPropertyValuesToInsert(entity, GetMergeMap(anything), source); IType[] types = persister.PropertyTypes; bool substitute = await(SubstituteValuesIfNecessaryAsync(entity, id, values, persister, source, cancellationToken)).ConfigureAwait(false); if (persister.HasCollections) { substitute = substitute || await(VisitCollectionsBeforeSaveAsync(entity, id, values, types, source, cancellationToken)).ConfigureAwait(false); } if (substitute) { persister.SetPropertyValues(entity, values); } TypeHelper.DeepCopy(values, types, persister.PropertyUpdateability, values, source); await(new ForeignKeys.Nullifier(entity, false, useIdentityColumn, source).NullifyTransientReferencesAsync(values, types, cancellationToken)).ConfigureAwait(false); 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"); await(source.ActionQueue.ExecuteAsync(insert, cancellationToken)).ConfigureAwait(false); id = insert.GeneratedId; //now done in EntityIdentityInsertAction //persister.setIdentifier( entity, id, source.getEntityMode() ); key = source.GenerateEntityKey(id, persister); source.PersistenceContext.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.ActionQueue.AddAction(insert); key = insert.DelayedEntityKey; } } object version = Versioning.GetVersion(values, persister); source.PersistenceContext.AddEntity( entity, persister.IsMutable ? Status.Loaded : Status.ReadOnly, values, key, version, LockMode.Write, useIdentityColumn, persister, VersionIncrementDisabled, false); //source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) ); if (!useIdentityColumn) { source.ActionQueue.AddAction(new EntityInsertAction(id, values, entity, version, persister, source)); } await(CascadeAfterSaveAsync(source, persister, entity, anything, cancellationToken)).ConfigureAwait(false); MarkInterceptorDirty(entity, persister, source); return(id); }
protected virtual void PerformUpdate(SaveOrUpdateEvent @event, object entity, IEntityPersister persister) { if (!persister.IsMutable) { log.Debug("immutable instance passed to PerformUpdate(), locking"); } if (log.IsDebugEnabled) { log.Debug("updating " + MessageHelper.InfoString(persister, @event.RequestedId, @event.Session.Factory)); } IEventSource source = @event.Session; EntityKey key = source.GenerateEntityKey(@event.RequestedId, persister); 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, persister.IsMutable ? Status.Loaded : Status.ReadOnly, null, key, persister.GetVersion(entity), 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); }
/// <summary> /// Performs all the actual work needed to save an entity (well to get the save moved to /// the execution queue). /// </summary> /// <param name="entity">The entity to be saved </param> /// <param name="key">The id to be used for saving the entity (or null, in the case of identity columns) </param> /// <param name="persister">The entity's persister instance. </param> /// <param name="useIdentityColumn">Should an identity column be used for id generation? </param> /// <param name="anything">Generally cascade-specific information. </param> /// <param name="source">The session which is the source of the current event. </param> /// <param name="requiresImmediateIdAccess"> /// Is access to the identifier required immediately /// after the completion of the save? persist(), for example, does not require this... /// </param> /// <returns> /// The id used to save the entity; may be null depending on the /// type of id generator used and the requiresImmediateIdAccess value /// </returns> protected virtual object PerformSaveOrReplicate(object entity, EntityKey key, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess) { Validate(entity, persister, source); object id = key == null ? null : key.Identifier; // NH Different behavior (shouldDelayIdentityInserts=false anyway) //bool inTxn = source.ConnectionManager.IsInActiveTransaction; //bool shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess; bool shouldDelayIdentityInserts = false; // 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.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false, false); CascadeBeforeSave(source, persister, entity, anything); // NH-962: This was originally done before many-to-one cascades. if (useIdentityColumn && !shouldDelayIdentityInserts) { log.Debug("executing insertions"); source.ActionQueue.ExecuteInserts(); } object[] values = persister.GetPropertyValuesToInsert(entity, GetMergeMap(anything), source); IType[] types = persister.PropertyTypes; bool 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.EntityMode); } TypeHelper.DeepCopy(values, types, persister.PropertyUpdateability, 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.ActionQueue.Execute(insert); id = insert.GeneratedId; //now done in EntityIdentityInsertAction //persister.setIdentifier( entity, id, source.getEntityMode() ); key = source.GenerateEntityKey(id, persister); source.PersistenceContext.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.ActionQueue.AddAction(insert); key = insert.DelayedEntityKey; } } object version = Versioning.GetVersion(values, persister); source.PersistenceContext.AddEntity( entity, persister.IsMutable ? Status.Loaded : Status.ReadOnly, values, key, version, LockMode.Write, useIdentityColumn, persister, VersionIncrementDisabled, false); //source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) ); if (!useIdentityColumn) { source.ActionQueue.AddAction(new EntityInsertAction(id, values, entity, version, persister, source)); } CascadeAfterSave(source, persister, entity, anything); MarkInterceptorDirty(entity, persister, source); return id; }
/// <summary> /// Prepares the save call by checking the session caches for a pre-existing /// entity and performing any lifecycle callbacks. /// </summary> /// <param name="entity">The entity to be saved. </param> /// <param name="id">The id by which to save the entity. </param> /// <param name="persister">The entity's persister instance. </param> /// <param name="useIdentityColumn">Is an identity column being used? </param> /// <param name="anything">Generally cascade-specific information. </param> /// <param name="source">The session from which the event originated. </param> /// <param name="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). /// </param> /// <returns> /// The id used to save the entity; may be null depending on the /// type of id generator used and the requiresImmediateIdAccess value /// </returns> protected virtual object PerformSave(object entity, object id, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess) { if (log.IsDebugEnabled) { log.Debug("saving " + MessageHelper.InfoString(persister, id, source.Factory)); } EntityKey key; if (!useIdentityColumn) { key = source.GenerateEntityKey(id, persister); object old = source.PersistenceContext.GetEntity(key); if (old != null) { if (source.PersistenceContext.GetEntry(old).Status == Status.Deleted) { source.ForceFlush(source.PersistenceContext.GetEntry(old)); } else { throw new NonUniqueObjectException(id, persister.EntityName); } } persister.SetIdentifier(entity, id, source.EntityMode); } else { key = null; } if (InvokeSaveLifecycle(entity, persister, source)) { return id; //EARLY EXIT } return PerformSaveOrReplicate(entity, key, persister, useIdentityColumn, anything, source, requiresImmediateIdAccess); }
public virtual async Task OnReplicateAsync(ReplicateEvent @event, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); IEventSource source = @event.Session; if (source.PersistenceContext.ReassociateIfUninitializedProxy(@event.Entity)) { log.Debug("uninitialized proxy passed to replicate()"); return; } object entity = await(source.PersistenceContext.UnproxyAndReassociateAsync(@event.Entity, cancellationToken)).ConfigureAwait(false); if (source.PersistenceContext.IsEntryFor(entity)) { log.Debug("ignoring persistent instance passed to replicate()"); //hum ... should we cascade anyway? throw an exception? fine like it is? return; } IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity); // get the id from the object /*if ( persister.isUnsaved(entity, source) ) { * throw new TransientObjectException("transient instance passed to replicate()"); * }*/ object id = persister.GetIdentifier(entity); if (id == null) { throw new TransientObjectException("instance with null id passed to replicate()"); } ReplicationMode replicationMode = @event.ReplicationMode; object oldVersion; if (replicationMode == ReplicationMode.Exception) { //always do an INSERT, and let it fail by constraint violation oldVersion = null; } else { //what is the version on the database? oldVersion = await(persister.GetCurrentVersionAsync(id, source, cancellationToken)).ConfigureAwait(false); } if (oldVersion != null) { if (log.IsDebugEnabled()) { log.Debug("found existing row for {0}", MessageHelper.InfoString(persister, id, source.Factory)); } // HHH-2378 object realOldVersion = persister.IsVersioned ? oldVersion : null; bool canReplicate = replicationMode.ShouldOverwriteCurrentVersion(entity, realOldVersion, persister.GetVersion(entity), persister.VersionType); if (canReplicate) { //will result in a SQL UPDATE: await(PerformReplicationAsync(entity, id, realOldVersion, persister, replicationMode, source, cancellationToken)).ConfigureAwait(false); } else { //else do nothing (don't even reassociate object!) log.Debug("no need to replicate"); } //TODO: would it be better to do a refresh from db? } else { // no existing row - do an insert if (log.IsDebugEnabled()) { log.Debug("no existing row, replicating new instance {0}", MessageHelper.InfoString(persister, id, source.Factory)); } bool regenerate = persister.IsIdentifierAssignedByInsert; // prefer re-generation of identity! EntityKey key = regenerate ? null : source.GenerateEntityKey(id, persister); await(PerformSaveOrReplicateAsync(entity, key, persister, regenerate, replicationMode, source, true, cancellationToken)).ConfigureAwait(false); } }
private void PerformReplication(object entity, object id, object version, IEntityPersister persister, ReplicationMode replicationMode, IEventSource source) { if (log.IsDebugEnabled) { log.Debug("replicating changes to " + MessageHelper.InfoString(persister, id, source.Factory)); } new OnReplicateVisitor(source, id, entity, true).Process(entity, persister); source.PersistenceContext.AddEntity( entity, persister.IsMutable ? Status.Loaded : Status.ReadOnly, null, source.GenerateEntityKey(id, persister), version, LockMode.None, true, persister, true, false); CascadeAfterReplicate(entity, persister, replicationMode, source); }
public virtual void OnDelete(DeleteEvent @event, ISet <object> transientEntities) { IEventSource source = @event.Session; IPersistenceContext persistenceContext = source.PersistenceContext; object entity = persistenceContext.UnproxyAndReassociate(@event.Entity); EntityEntry entityEntry = persistenceContext.GetEntry(entity); IEntityPersister persister; object id; object version; if (entityEntry == null) { log.Debug("entity was not persistent in delete processing"); persister = source.GetEntityPersister(@event.EntityName, entity); if (ForeignKeys.IsTransientSlow(persister.EntityName, entity, source)) { DeleteTransientEntity(source, entity, @event.CascadeDeleteEnabled, persister, transientEntities); // EARLY EXIT!!! return; } else { PerformDetachedEntityDeletionCheck(@event); } id = persister.GetIdentifier(entity); if (id == null) { throw new TransientObjectException("the detached instance passed to delete() had a null identifier"); } EntityKey key = source.GenerateEntityKey(id, persister); persistenceContext.CheckUniqueness(key, entity); new OnUpdateVisitor(source, id, entity).Process(entity, persister); version = persister.GetVersion(entity); entityEntry = persistenceContext.AddEntity( entity, persister.IsMutable ? Status.Loaded : Status.ReadOnly, persister.GetPropertyValues(entity), key, version, LockMode.None, true, persister, false); } else { log.Debug("deleting a persistent instance"); if (entityEntry.Status == Status.Deleted || entityEntry.Status == Status.Gone) { log.Debug("object was already deleted"); return; } persister = entityEntry.Persister; id = entityEntry.Id; version = entityEntry.Version; } if (InvokeDeleteLifecycle(source, entity, persister)) { return; } DeleteEntity(source, entity, entityEntry, @event.CascadeDeleteEnabled, persister, transientEntities); if (source.Factory.Settings.IsIdentifierRollbackEnabled) { persister.ResetIdentifier(entity, id, version); } }
public virtual async Task OnMergeAsync(MergeEvent @event, IDictionary copiedAlready, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); EventCache copyCache = (EventCache)copiedAlready; IEventSource source = @event.Session; object original = @event.Original; if (original != null) { object entity; if (original.IsProxy()) { ILazyInitializer li = ((INHibernateProxy)original).HibernateLazyInitializer; if (li.IsUninitialized) { log.Debug("ignoring uninitialized proxy"); @event.Result = await(source.LoadAsync(li.EntityName, li.Identifier, cancellationToken)).ConfigureAwait(false); return; //EARLY EXIT! } else { entity = await(li.GetImplementationAsync(cancellationToken)).ConfigureAwait(false); } } else { entity = original; } if (copyCache.Contains(entity) && copyCache.IsOperatedOn(entity)) { log.Debug("already in merge process"); @event.Result = entity; } else { if (copyCache.Contains(entity)) { log.Info("already in copyCache; setting in merge process"); copyCache.SetOperatedOn(entity, true); } @event.Entity = entity; EntityState entityState = EntityState.Undefined; if (ReferenceEquals(null, @event.EntityName)) { @event.EntityName = source.BestGuessEntityName(entity); } // Check the persistence context for an entry relating to this // entity to be merged... EntityEntry entry = source.PersistenceContext.GetEntry(entity); if (entry == null) { IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity); object id = persister.GetIdentifier(entity); if (id != null) { EntityKey key = source.GenerateEntityKey(id, persister); object managedEntity = source.PersistenceContext.GetEntity(key); entry = source.PersistenceContext.GetEntry(managedEntity); if (entry != null) { // we have specialized case of a detached entity from the // perspective of the merge operation. Specifically, we // have an incoming entity instance which has a corresponding // entry in the current persistence context, but registered // under a different entity instance entityState = EntityState.Detached; } } } if (entityState == EntityState.Undefined) { entityState = await(GetEntityStateAsync(entity, @event.EntityName, entry, source, cancellationToken)).ConfigureAwait(false); } switch (entityState) { case EntityState.Persistent: await(EntityIsPersistentAsync(@event, copyCache, cancellationToken)).ConfigureAwait(false); break; case EntityState.Transient: await(EntityIsTransientAsync(@event, copyCache, cancellationToken)).ConfigureAwait(false); break; case EntityState.Detached: await(EntityIsDetachedAsync(@event, copyCache, cancellationToken)).ConfigureAwait(false); break; default: throw new ObjectDeletedException("deleted instance passed to merge", null, GetLoggableName(@event.EntityName, entity)); } } } }
private async Task <object> AssembleCacheEntryAsync(CacheEntry entry, object id, IEntityPersister persister, LoadEvent @event, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); object optionalObject = @event.InstanceToLoad; IEventSource session = @event.Session; ISessionFactoryImplementor factory = session.Factory; if (log.IsDebugEnabled()) { log.Debug("assembling entity from second-level cache: {0}", MessageHelper.InfoString(persister, id, factory)); } IEntityPersister subclassPersister = factory.GetEntityPersister(entry.Subclass); object result = optionalObject ?? session.Instantiate(subclassPersister, id); // make it circular-reference safe EntityKey entityKey = session.GenerateEntityKey(id, subclassPersister); TwoPhaseLoad.AddUninitializedCachedEntity(entityKey, result, subclassPersister, LockMode.None, entry.Version, session); IType[] types = subclassPersister.PropertyTypes; object[] values = await(entry.AssembleAsync(result, id, subclassPersister, session.Interceptor, session, cancellationToken)).ConfigureAwait(false); // intializes result by side-effect TypeHelper.DeepCopy(values, types, subclassPersister.PropertyUpdateability, values, session); object version = Versioning.GetVersion(values, subclassPersister); if (log.IsDebugEnabled()) { log.Debug("Cached Version: {0}", version); } IPersistenceContext persistenceContext = session.PersistenceContext; bool isReadOnly = session.DefaultReadOnly; if (persister.IsMutable) { object proxy = persistenceContext.GetProxy(entityKey); if (proxy != null) { // this is already a proxy for this impl // only set the status to read-only if the proxy is read-only isReadOnly = ((INHibernateProxy)proxy).HibernateLazyInitializer.ReadOnly; } } else { isReadOnly = true; } persistenceContext.AddEntry( result, isReadOnly ? Status.ReadOnly : Status.Loaded, values, null, id, version, LockMode.None, true, subclassPersister, false); subclassPersister.AfterInitialize(result, session); await(persistenceContext.InitializeNonLazyCollectionsAsync(cancellationToken)).ConfigureAwait(false); // upgrade the lock if necessary: //lock(result, lockMode); //PostLoad is needed for EJB3 //TODO: reuse the PostLoadEvent... PostLoadEvent postLoadEvent = new PostLoadEvent(session); postLoadEvent.Entity = result; postLoadEvent.Id = id; postLoadEvent.Persister = persister; IPostLoadEventListener[] listeners = session.Listeners.PostLoadEventListeners; for (int i = 0; i < listeners.Length; i++) { listeners[i].OnPostLoad(postLoadEvent); } return(result); }
private static bool ExistsInDatabase(object entity, IEventSource source, IEntityPersister persister) { EntityEntry entry = source.PersistenceContext.GetEntry(entity); if (entry == null) { object id = persister.GetIdentifier(entity, source.EntityMode); if (id != null) { EntityKey key = source.GenerateEntityKey(id, persister); object managedEntity = source.PersistenceContext.GetEntity(key); entry = source.PersistenceContext.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.ExistsInDatabase; } }
public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready) { IEventSource source = @event.Session; bool isTransient = !source.Contains(@event.Entity); if (source.PersistenceContext.ReassociateIfUninitializedProxy(@event.Entity)) { if (isTransient) { source.SetReadOnly(@event.Entity, source.DefaultReadOnly); } return; } object obj = source.PersistenceContext.UnproxyAndReassociate(@event.Entity); if (refreshedAlready.Contains(obj)) { log.Debug("already refreshed"); return; } EntityEntry e = source.PersistenceContext.GetEntry(obj); IEntityPersister persister; object id; if (e == null) { persister = source.GetEntityPersister(null, obj); //refresh() does not pass an entityName id = persister.GetIdentifier(obj); if (log.IsDebugEnabled()) { log.Debug("refreshing transient {0}", MessageHelper.InfoString(persister, id, source.Factory)); } EntityKey key = source.GenerateEntityKey(id, persister); if (source.PersistenceContext.GetEntry(key) != null) { throw new PersistentObjectException("attempted to refresh transient instance when persistent instance was already associated with the Session: " + MessageHelper.InfoString(persister, id, source.Factory)); } } else { if (log.IsDebugEnabled()) { log.Debug("refreshing {0}", MessageHelper.InfoString(e.Persister, e.Id, source.Factory)); } if (!e.ExistsInDatabase) { throw new HibernateException("this instance does not yet exist as a row in the database"); } persister = e.Persister; id = e.Id; } // cascade the refresh prior to refreshing this entity refreshedAlready[obj] = obj; new Cascade(CascadingAction.Refresh, CascadePoint.BeforeRefresh, source).CascadeOn(persister, obj, refreshedAlready); if (e != null) { EntityKey key = source.GenerateEntityKey(id, persister); source.PersistenceContext.RemoveEntity(key); if (persister.HasCollections) { new EvictVisitor(source).Process(obj, persister); } } if (persister.HasCache) { CacheKey ck = source.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); persister.Cache.Remove(ck); } EvictCachedCollections(persister, id, source.Factory); // NH Different behavior : NH-1601 // At this point the entity need the real refresh, all elementes of collections are Refreshed, // the collection state was evicted, but the PersistentCollection (in the entity state) // is associated with a possible previous session. new WrapVisitor(source).Process(obj, persister); string previousFetchProfile = source.FetchProfile; source.FetchProfile = "refresh"; object result = persister.Load(id, obj, @event.LockMode, source); if (result != null) { if (!persister.IsMutable) { source.SetReadOnly(result, true); } else { source.SetReadOnly(result, e == null ? source.DefaultReadOnly : !e.IsModifiableEntity()); } } source.FetchProfile = previousFetchProfile; // NH Different behavior : we are ignoring transient entities without throw any kind of exception // because a transient entity is "self refreshed" if (!ForeignKeys.IsTransientFast(persister.EntityName, obj, @event.Session).GetValueOrDefault(result == null)) { UnresolvableObjectException.ThrowIfNull(result, id, persister.EntityName); } }
/// <summary> /// Perform the entity deletion. Well, as with most operations, does not /// really perform it; just schedules an action/execution with the /// <see cref="ActionQueue"/> for execution during flush. /// </summary> /// <param name="session">The originating session </param> /// <param name="entity">The entity to delete </param> /// <param name="entityEntry">The entity's entry in the <see cref="ISession"/> </param> /// <param name="isCascadeDeleteEnabled">Is delete cascading enabled? </param> /// <param name="persister">The entity persister. </param> /// <param name="transientEntities">A cache of already deleted entities. </param> protected virtual void DeleteEntity(IEventSource session, object entity, EntityEntry entityEntry, bool isCascadeDeleteEnabled, IEntityPersister persister, ISet<object> transientEntities) { if (log.IsDebugEnabled) { log.Debug("deleting " + MessageHelper.InfoString(persister, entityEntry.Id, session.Factory)); } IPersistenceContext persistenceContext = session.PersistenceContext; IType[] propTypes = persister.PropertyTypes; object version = entityEntry.Version; object[] currentState; if (entityEntry.LoadedState == null) { //ie. the entity came in from update() currentState = persister.GetPropertyValues(entity, session.EntityMode); } else { currentState = entityEntry.LoadedState; } object[] deletedState = CreateDeletedState(persister, currentState, session); entityEntry.DeletedState = deletedState; session.Interceptor.OnDelete(entity, entityEntry.Id, deletedState, persister.PropertyNames, propTypes); // before any callbacks, etc, so subdeletions see that this deletion happened first persistenceContext.SetEntryStatus(entityEntry, Status.Deleted); EntityKey key = session.GenerateEntityKey(entityEntry.Id, persister); CascadeBeforeDelete(session, persister, entity, entityEntry, transientEntities); new ForeignKeys.Nullifier(entity, true, false, session).NullifyTransientReferences(entityEntry.DeletedState, propTypes); new Nullability(session).CheckNullability(entityEntry.DeletedState, persister, true); persistenceContext.NullifiableEntityKeys.Add(key); // Ensures that containing deletions happen before sub-deletions session.ActionQueue.AddAction(new EntityDeleteAction(entityEntry.Id, deletedState, version, entity, persister, isCascadeDeleteEnabled, session)); CascadeAfterDelete(session, persister, entity, transientEntities); // the entry will be removed after the flush, and will no longer // override the stale snapshot // This is now handled by removeEntity() in EntityDeleteAction //persistenceContext.removeDatabaseSnapshot(key); }