protected virtual void EntityIsDetached(MergeEvent @event, IDictionary copyCache) { log.Debug("merging detached instance"); object entity = @event.Entity; IEventSource source = @event.Session; IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity); string entityName = persister.EntityName; object id = @event.RequestedId; if (id == null) { id = persister.GetIdentifier(entity, source.EntityMode); } else { // check that entity id = requestedId object entityId = persister.GetIdentifier(entity, source.EntityMode); if (!persister.IdentifierType.IsEqual(id, entityId, source.EntityMode, source.Factory)) { throw new HibernateException("merge requested with id not matching id of passed entity"); } } string previousFetchProfile = source.FetchProfile; source.FetchProfile = "merge"; //we must clone embedded composite identifiers, or //we will get back the same instance that we pass in object clonedIdentifier = persister.IdentifierType.DeepCopy(id, source.EntityMode, source.Factory); object result = source.Get(persister.EntityName, clonedIdentifier); source.FetchProfile = previousFetchProfile; if (result == null) { //TODO: we should throw an exception if we really *know* for sure // that this is a detached instance, rather than just assuming //throw new StaleObjectStateException(entityName, id); // we got here because we assumed that an instance // with an assigned id was detached, when it was // really persistent EntityIsTransient(@event, copyCache); } else { // NH different behavior : NH-1517 if (InvokeUpdateLifecycle(entity, persister, source)) { return; } ((EventCache)copyCache).Add(entity, result, true); //before cascade! object target = source.PersistenceContext.Unproxy(result); if (target == entity) { throw new AssertionFailure("entity was not detached"); } else if (!source.GetEntityName(target).Equals(entityName)) { throw new WrongClassException("class of the given object did not match class of persistent copy", @event.RequestedId, persister.EntityName); } else if (IsVersionChanged(entity, source, persister, target)) { if (source.Factory.Statistics.IsStatisticsEnabled) { source.Factory.StatisticsImplementor.OptimisticFailure(entityName); } throw new StaleObjectStateException(persister.EntityName, id); } // cascade first, so that all unsaved objects get their // copy created before we actually copy CascadeOnMerge(source, persister, entity, copyCache); CopyValues(persister, entity, target, source, copyCache); //copyValues works by reflection, so explicitly mark the entity instance dirty MarkInterceptorDirty(entity, target); @event.Result = result; } }