public EntityIdentityInsertAction(object[] state, object instance, IEntityPersister persister, ISessionImplementor session, bool isDelayed) : base(session, null, instance, persister) { this.state = state; this.isDelayed = isDelayed; delayedEntityKey = this.isDelayed ? GenerateDelayedEntityKey() : null; }
/// <summary> /// If an EntityKey represents a batch loadable entity, add /// it to the queue. /// </summary> /// <remarks> /// Note that the contract here is such that any key passed in should /// previously have been been checked for existence within the /// <see cref="ISession" />; failure to do so may cause the /// referenced entity to be included in a batch even though it is /// already associated with the <see cref="ISession" />. /// </remarks> public void AddBatchLoadableEntityKey(EntityKey key) { if (key.IsBatchLoadable) { batchLoadableEntityKeys[key] = Marker; } }
/// <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> /// <returns> An EntityEntry representing the entity within this session. </returns> protected EntityEntry Reassociate(AbstractEvent @event, object entity, object id, IEntityPersister persister) { if (log.IsDebugEnabled) { log.Debug("Reassociating transient instance: " + MessageHelper.InfoString(persister, id, @event.Session.Factory)); } IEventSource source = @event.Session; EntityKey key = new EntityKey(id, persister, source.EntityMode); source.PersistenceContext.CheckUniqueness(key, entity); //get a snapshot object[] values = persister.GetPropertyValues(entity, source.EntityMode); TypeFactory.DeepCopy(values, persister.PropertyTypes, persister.PropertyUpdateability, values, source); object version = Versioning.GetVersion(values, persister); EntityEntry newEntry = source.PersistenceContext.AddEntity(entity, Status.Loaded, values, key, version, LockMode.None, true, persister, false, true); new OnLockVisitor(source, id, entity).Process(entity, persister); persister.AfterReassociate(entity, source); return newEntry; }
private static void ProcessDereferencedCollection(IPersistentCollection coll, ISessionImplementor session) { IPersistenceContext persistenceContext = session.PersistenceContext; CollectionEntry entry = persistenceContext.GetCollectionEntry(coll); ICollectionPersister loadedPersister = entry.LoadedPersister; if (log.IsDebugEnabled && loadedPersister != null) log.Debug("Collection dereferenced: " + MessageHelper.InfoString(loadedPersister, entry.LoadedKey, session.Factory)); // do a check bool hasOrphanDelete = loadedPersister != null && loadedPersister.HasOrphanDelete; if (hasOrphanDelete) { object ownerId = loadedPersister.OwnerEntityPersister.GetIdentifier(coll.Owner, session.EntityMode); // TODO NH Different behavior //if (ownerId == null) //{ // // the owning entity may have been deleted and its identifier unset due to // // identifier-rollback; in which case, try to look up its identifier from // // the persistence context // if (session.Factory.Settings.IsIdentifierRollbackEnabled) // { // EntityEntry ownerEntry = persistenceContext.GetEntry(coll.Owner); // if (ownerEntry != null) // { // ownerId = ownerEntry.Id; // } // } // if (ownerId == null) // { // throw new AssertionFailure("Unable to determine collection owner identifier for orphan-delete processing"); // } //} EntityKey key = new EntityKey(ownerId, loadedPersister.OwnerEntityPersister, session.EntityMode); object owner = persistenceContext.GetEntity(key); if (owner == null) { throw new AssertionFailure("collection owner not associated with session: " + loadedPersister.Role); } EntityEntry e = persistenceContext.GetEntry(owner); //only collections belonging to deleted entities are allowed to be dereferenced in the case of orphan delete if (e != null && e.Status != Status.Deleted && e.Status != Status.Gone) { throw new HibernateException("A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " + loadedPersister.Role); } } // do the work entry.CurrentPersister = null; entry.CurrentKey = null; PrepareCollectionForUpdate(coll, entry, session.EntityMode, session.Factory); }
protected virtual void DoEvict(object obj, EntityKey key, IEntityPersister persister, IEventSource session) { if (log.IsDebugEnabled) { log.Debug("evicting " + MessageHelper.InfoString(persister)); } // remove all collections for the entity from the session-level cache if (persister.HasCollections) { new EvictVisitor(session).Process(obj, persister); } new Cascade(CascadingAction.Evict, CascadePoint.AfterEvict, session).CascadeOn(persister, obj); }
public override bool IsNull(object owner, ISessionImplementor session) { if (propertyName != null) { IEntityPersister ownerPersister = session.Factory.GetEntityPersister(entityName); object id = session.GetContextEntityIdentifier(owner); EntityKey entityKey = new EntityKey(id, ownerPersister, session.EntityMode); return session.PersistenceContext.IsPropertyNull(entityKey, PropertyName); } else { return false; } }
public virtual void OnEvict(EvictEvent @event) { IEventSource source = @event.Session; object obj = @event.Entity; IPersistenceContext persistenceContext = source.PersistenceContext; if (obj is INHibernateProxy) { ILazyInitializer li = ((INHibernateProxy)obj).HibernateLazyInitializer; object id = li.Identifier; IEntityPersister persister = source.Factory.GetEntityPersister(li.EntityName); if (id == null) { throw new ArgumentException("null identifier"); } EntityKey key = new EntityKey(id, persister, source.EntityMode); persistenceContext.RemoveProxy(key); if (!li.IsUninitialized) { object entity = persistenceContext.RemoveEntity(key); if (entity != null) { EntityEntry e = @event.Session.PersistenceContext.RemoveEntry(entity); DoEvict(entity, key, e.Persister, @event.Session); } } li.Session = null; } else { EntityEntry e = persistenceContext.RemoveEntry(obj); if (e != null) { EntityKey key = new EntityKey(e.Id, e.Persister, source.EntityMode); persistenceContext.RemoveEntity(key); DoEvict(obj, key, e.Persister, source); } } }
/// <summary> Adds an entity to the internal caches.</summary> public EntityEntry AddEntity(object entity, Status status, object[] loadedState, EntityKey entityKey, object version, LockMode lockMode, bool existsInDatabase, IEntityPersister persister, bool disableVersionIncrement, bool lazyPropertiesAreUnfetched) { AddEntity(entityKey, entity); return AddEntry(entity, status, loadedState, null, entityKey.Identifier, version, lockMode, existsInDatabase, persister, disableVersionIncrement, lazyPropertiesAreUnfetched); }
/// <summary> /// Associate a proxy that was instantiated by another session with this session /// </summary> /// <param name="li">The proxy initializer. </param> /// <param name="proxy">The proxy to reassociate. </param> private void ReassociateProxy(ILazyInitializer li, INHibernateProxy proxy) { if (li.Session != Session) { IEntityPersister persister = session.Factory.GetEntityPersister(li.EntityName); EntityKey key = new EntityKey(li.Identifier, persister, session.EntityMode); // any earlier proxy takes precedence if (!proxiesByKey.ContainsKey(key)) { proxiesByKey[key] = proxy; } proxy.HibernateLazyInitializer.Session = Session; } }
/// <summary> /// Hydrate the state of an object from the SQL <c>IDataReader</c>, into /// an array of "hydrated" values (do not resolve associations yet), /// and pass the hydrated state to the session. /// </summary> private void LoadFromResultSet(IDataReader rs, int i, object obj, string instanceClass, EntityKey key, string rowIdAlias, LockMode lockMode, ILoadable rootPersister, ISessionImplementor session) { object id = key.Identifier; // Get the persister for the _subclass_ ILoadable persister = (ILoadable) Factory.GetEntityPersister(instanceClass); if (log.IsDebugEnabled) { log.Debug("Initializing object from DataReader: " + MessageHelper.InfoString(persister, id)); } bool eagerPropertyFetch = IsEagerPropertyFetchEnabled(i); // add temp entry so that the next step is circular-reference // safe - only needed because some types don't take proper // advantage of two-phase-load (esp. components) TwoPhaseLoad.AddUninitializedEntity(key, obj, persister, lockMode, !eagerPropertyFetch, session); // This is not very nice (and quite slow): string[][] cols = persister == rootPersister ? EntityAliases[i].SuffixedPropertyAliases : EntityAliases[i].GetSuffixedPropertyAliases(persister); object[] values = persister.Hydrate(rs, id, obj, rootPersister, cols, eagerPropertyFetch, session); object rowId = persister.HasRowId ? rs[rowIdAlias] : null; IAssociationType[] ownerAssociationTypes = OwnerAssociationTypes; if (ownerAssociationTypes != null && ownerAssociationTypes[i] != null) { string ukName = ownerAssociationTypes[i].RHSUniqueKeyPropertyName; if (ukName != null) { int index = ((IUniqueKeyLoadable) persister).GetPropertyIndex(ukName); IType type = persister.PropertyTypes[index]; // polymorphism not really handled completely correctly, // perhaps...well, actually its ok, assuming that the // entity name used in the lookup is the same as the // the one used here, which it will be EntityUniqueKey euk = new EntityUniqueKey(rootPersister.EntityName, ukName, type.SemiResolve(values[index], session, obj), type, session.EntityMode, session.Factory); session.PersistenceContext.AddEntity(euk, obj); } } TwoPhaseLoad.PostHydrate(persister, id, values, rowId, obj, lockMode, !eagerPropertyFetch, session); }
public AssociationKey(EntityKey ownerKey, string propertyName) { this.ownerKey = ownerKey; this.propertyName = propertyName; }
/// <summary> /// For missing objects associated by one-to-one with another object in the /// result set, register the fact that the the object is missing with the /// session. /// </summary> private void RegisterNonExists(EntityKey[] keys, ISessionImplementor session) { int[] owners = Owners; if (owners != null) { EntityType[] ownerAssociationTypes = OwnerAssociationTypes; for (int i = 0; i < keys.Length; i++) { int owner = owners[i]; if (owner > -1) { EntityKey ownerKey = keys[owner]; if (keys[i] == null && ownerKey != null) { bool isOneToOneAssociation = ownerAssociationTypes != null && ownerAssociationTypes[i] != null && ownerAssociationTypes[i].IsOneToOne; if (isOneToOneAssociation) { session.PersistenceContext.AddNullProperty(ownerKey, ownerAssociationTypes[i].PropertyName); } } } } } }
/// <summary> /// The entity instance is already in the session cache /// </summary> private void InstanceAlreadyLoaded(IDataReader rs, int i, IEntityPersister persister, EntityKey key, object obj, LockMode lockMode, ISessionImplementor session) { if (!persister.IsInstance(obj, session.EntityMode)) { string errorMsg = string.Format("loading object was of wrong class [{0}]", obj.GetType().FullName); throw new WrongClassException(errorMsg, key.Identifier, persister.EntityName); } if (LockMode.None != lockMode && UpgradeLocks()) { EntityEntry entry = session.PersistenceContext.GetEntry(obj); bool isVersionCheckNeeded = persister.IsVersioned && entry.LockMode.LessThan(lockMode); // we don't need to worry about existing version being uninitialized // because this block isn't called by a re-entrant load (re-entrant // load _always_ have lock mode NONE if (isVersionCheckNeeded) { // we only check the version when _upgrading_ lock modes CheckVersion(i, persister, key.Identifier, obj, rs, session); // we need to upgrade the lock mode to the mode requested entry.LockMode = lockMode; } } }
/// <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> /// Checks whether the entity key was already checked in the cache. /// </summary> /// <param name="persister">The entity persister.</param> /// <param name="key">The entity key.</param> /// <returns><see langword="true"/> whether the entity key was checked, <see langword="false"/> otherwise.</returns> internal bool WasEntityKeyChecked(IEntityPersister persister, EntityKey key) { return(_queryCheckedEntityKeys.TryGetValue(persister.EntityName, out var checkedKeys) && checkedKeys.Contains(key)); }
/// <summary> /// Attempts to check whether the given key represents an entity already loaded within the /// current session. /// </summary> /// <param name="obj">The entity reference against which to perform the uniqueness check.</param> /// <param name="key">The entity key.</param> public void CheckUniqueness(EntityKey key, object obj) { object entity = GetEntity(key); if (entity == obj) { throw new AssertionFailure("object already associated, but no entry was found"); } if (entity != null) { throw new NonUniqueObjectException(key.Identifier, key.EntityName); } }
/// <summary> /// Return the existing proxy associated with the given <tt>EntityKey</tt>, or the /// third argument (the entity associated with the key) if no proxy exists. Init /// the proxy to the target implementation, if necessary. /// </summary> public object ProxyFor(IEntityPersister persister, EntityKey key, object impl) { if (!persister.HasProxy || key == null) return impl; INHibernateProxy proxy; if (proxiesByKey.TryGetValue(key, out proxy)) { return NarrowProxy(proxy, persister, key, impl); } else { return impl; } }
/// <summary> /// Add an uninitialized instance of an entity class, as a placeholder to ensure object /// identity. Must be called before <tt>postHydrate()</tt>. /// Create a "temporary" entry for a newly instantiated entity. The entity is uninitialized, /// but we need the mapping from id to instance in order to guarantee uniqueness. /// </summary> public static void AddUninitializedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, ISessionImplementor session) { session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, null, lockMode, true, persister, false); }
/// <summary> /// Adds a subselect fetch decriptor for the given entity key. /// </summary> /// <param name="key">The entity for which to register the subselect fetch.</param> /// <param name="subquery">The fetch descriptor.</param> public void AddSubselect(EntityKey key, SubselectFetch subquery) { subselectsByEntityKey[key] = subquery; }
/// <summary> /// After evicting or deleting an entity, we don't need to /// know the query that was used to load it anymore (don't /// call this after loading the entity, since we might still /// need to load its collections) /// </summary> public void RemoveSubselect(EntityKey key) { subselectsByEntityKey.Remove(key); }
private static Task ProcessDereferencedCollectionAsync(IPersistentCollection coll, ISessionImplementor session, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <object>(cancellationToken)); } try { IPersistenceContext persistenceContext = session.PersistenceContext; CollectionEntry entry = persistenceContext.GetCollectionEntry(coll); ICollectionPersister loadedPersister = entry.LoadedPersister; if (log.IsDebugEnabled && loadedPersister != null) { log.Debug("Collection dereferenced: " + MessageHelper.CollectionInfoString(loadedPersister, coll, entry.LoadedKey, session)); } // do a check bool hasOrphanDelete = loadedPersister != null && loadedPersister.HasOrphanDelete; if (hasOrphanDelete) { object ownerId = loadedPersister.OwnerEntityPersister.GetIdentifier(coll.Owner); // TODO NH Different behavior //if (ownerId == null) //{ // // the owning entity may have been deleted and its identifier unset due to // // identifier-rollback; in which case, try to look up its identifier from // // the persistence context // if (session.Factory.Settings.IsIdentifierRollbackEnabled) // { // EntityEntry ownerEntry = persistenceContext.GetEntry(coll.Owner); // if (ownerEntry != null) // { // ownerId = ownerEntry.Id; // } // } // if (ownerId == null) // { // throw new AssertionFailure("Unable to determine collection owner identifier for orphan-delete processing"); // } //} EntityKey key = session.GenerateEntityKey(ownerId, loadedPersister.OwnerEntityPersister); object owner = persistenceContext.GetEntity(key); if (owner == null) { return(Task.FromException <object>(new AssertionFailure("collection owner not associated with session: " + loadedPersister.Role))); } EntityEntry e = persistenceContext.GetEntry(owner); //only collections belonging to deleted entities are allowed to be dereferenced in the case of orphan delete if (e != null && e.Status != Status.Deleted && e.Status != Status.Gone) { return(Task.FromException <object>(new HibernateException("A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " + loadedPersister.Role))); } } // do the work entry.CurrentPersister = null; entry.CurrentKey = null; return(PrepareCollectionForUpdateAsync(coll, entry, session.Factory, cancellationToken)); } catch (System.Exception ex) { return(Task.FromException <object>(ex)); } }
public static void AddUninitializedCachedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, bool lazyPropertiesAreUnfetched, object version, ISessionImplementor session) { session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, version, lockMode, true, persister, false, lazyPropertiesAreUnfetched); }
/// <summary> /// If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy /// and overwrite the registration of the old one. This breaks == and occurs only for /// "class" proxies rather than "interface" proxies. Also init the proxy to point to /// the given target implementation if necessary. /// </summary> /// <param name="proxy">The proxy instance to be narrowed. </param> /// <param name="persister">The persister for the proxied entity. </param> /// <param name="key">The internal cache key for the proxied entity. </param> /// <param name="obj">(optional) the actual proxied entity instance. </param> /// <returns> An appropriately narrowed instance. </returns> public object NarrowProxy(INHibernateProxy proxy, IEntityPersister persister, EntityKey key, object obj) { bool alreadyNarrow = persister.GetConcreteProxyClass(session.EntityMode).IsAssignableFrom(proxy.GetType()); if (!alreadyNarrow) { if (ProxyWarnLog.IsWarnEnabled) { ProxyWarnLog.Warn("Narrowing proxy to " + persister.GetConcreteProxyClass(session.EntityMode) + " - this operation breaks =="); } if (obj != null) { proxiesByKey.Remove(key); return obj; //return the proxied object } else { proxy = (INHibernateProxy)persister.CreateProxy(key.Identifier, session); proxiesByKey[key] = proxy; //overwrite old proxy return proxy; } } else { if (obj != null) { proxy.HibernateLazyInitializer.SetImplementation(obj); } return proxy; } }
/// <summary> /// Return the Underlying Persistent Object in a given <see cref="ISession"/>, or null. /// </summary> /// <param name="s">The Session to get the object from.</param> /// <returns>The Persistent Object this proxy is Proxying, or <see langword="null" />.</returns> public object GetImplementation(ISessionImplementor s) { EntityKey key = new EntityKey(Identifier, s.Factory.GetEntityPersister(PersistentClass)); return s.GetEntity(key); }
public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready) { IEventSource source = @event.Session; if (source.PersistenceContext.ReassociateIfUninitializedProxy(@event.Entity)) 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, source.EntityMode); if (log.IsDebugEnabled) { log.Debug("refreshing transient " + MessageHelper.InfoString(persister, id, source.Factory)); } EntityKey key = new EntityKey(id, persister, source.EntityMode); 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 " + 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 = new EntityKey(id, persister, source.EntityMode); source.PersistenceContext.RemoveEntity(key); if (persister.HasCollections) new EvictVisitor(source).Process(obj, persister); } if (persister.HasCache) { CacheKey ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, source.EntityMode, source.Factory); 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); 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.IsTransient(persister.EntityName, obj, result == null, @event.Session)) UnresolvableObjectException.ThrowIfNull(result, id, persister.EntityName); }
internal object GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, bool returnProxies) { ILoadable[] persisters = EntityPersisters; int entitySpan = persisters.Length; for (int i = 0; i < entitySpan; i++) { keys[i] = GetKeyFromResultSet(i, persisters[i], i == entitySpan - 1 ? queryParameters.OptionalId : null, resultSet, session); //TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad) } RegisterNonExists(keys, session); // this call is side-effecty object[] row = GetRow(resultSet, persisters, keys, queryParameters.OptionalObject, optionalObjectKey, lockModeArray, hydratedObjects, session); ReadCollectionElements(row, resultSet, session); if (returnProxies) { // now get an existing proxy for each row element (if there is one) for (int i = 0; i < entitySpan; i++) { object entity = row[i]; object proxy = session.PersistenceContext.ProxyFor(persisters[i], keys[i], entity); if (entity != proxy) { // Force the proxy to resolve itself ((INHibernateProxy) proxy).HibernateLazyInitializer.SetImplementation(entity); row[i] = proxy; } } } return GetResultColumnOrRow(row, queryParameters.ResultTransformer, resultSet, session); }
/// <summary> /// Invokes the method if this is something that the LazyInitializer can handle /// without the underlying proxied object being instantiated. /// </summary> /// <param name="method">The name of the method/property to Invoke.</param> /// <param name="args">The arguments to pass the method/property.</param> /// <param name="proxy">The proxy object that the method is being invoked on.</param> /// <returns> /// The result of the Invoke if the underlying proxied object is not needed. If the /// underlying proxied object is needed then it returns the result <see cref="InvokeImplementation"/> /// which indicates that the Proxy will need to forward to the real implementation. /// </returns> public virtual object Invoke(MethodBase method, object[] args, object proxy) { string methodName = method.Name; int paramCount = method.GetParameters().Length; if (paramCount == 0) { if (!_overridesEquals && methodName == "GetHashCode") { return IdentityHashCodeProvider.GetHashCode(proxy); } else if (method.Equals(_getIdentifierMethod)) { return _id; } else if (methodName == "Finalize") { return null; } } else if (paramCount == 1) { if (!_overridesEquals && methodName == "Equals") { return args[0] == proxy; } else if (method.Equals(_setIdentifierMethod)) { Initialize(); _id = args[0]; return InvokeImplementation; } } else if (paramCount == 2) { // if the Proxy Engine delegates the call of GetObjectData to the Initializer // then we need to handle it. Castle.DynamicProxy takes care of serializing // proxies for us, but other providers might not. if (methodName == "GetObjectData") { SerializationInfo info = (SerializationInfo) args[0]; StreamingContext context = (StreamingContext) args[1]; // not used !?! if (_target == null & _session != null) { EntityKey key = new EntityKey(_id, _session.Factory.GetEntityPersister(_persistentClass)); _target = _session.GetEntity(key); } // let the specific LazyInitializer write its requirements for deserialization // into the stream. AddSerializationInfo(info, context); // don't need a return value for proxy. return null; } } return InvokeImplementation; }
/// <summary> /// Retrieve the fetch descriptor associated with the given entity key. /// </summary> /// <param name="key">The entity key for which to locate any defined subselect fetch.</param> /// <returns>The fetch descriptor; may return null if no subselect fetch queued for /// this entity key.</returns> public SubselectFetch GetSubselect(EntityKey key) { SubselectFetch result; subselectsByEntityKey.TryGetValue(key, out result); return result; }
public void Delete(object id, object version, object obj, ISessionImplementor session) { int span = TableSpan; bool isImpliedOptimisticLocking = !entityMetamodel.IsVersioned && entityMetamodel.OptimisticLockMode > Versioning.OptimisticLock.Version; object[] loadedState = null; if (isImpliedOptimisticLocking) { // need to treat this as if it where optimistic-lock="all" (dirty does *not* make sense); // first we need to locate the "loaded" state // // Note, it potentially could be a proxy, so perform the location the safe way... EntityKey key = new EntityKey(id, this, session.EntityMode); object entity = session.PersistenceContext.GetEntity(key); if (entity != null) { EntityEntry entry = session.PersistenceContext.GetEntry(entity); loadedState = entry.LoadedState; } } SqlCommandInfo[] deleteStrings; if (isImpliedOptimisticLocking && loadedState != null) { // we need to utilize dynamic delete statements deleteStrings = GenerateSQLDeleteStrings(loadedState); } else { // otherwise, utilize the static delete statements deleteStrings = SqlDeleteStrings; } for (int j = span - 1; j >= 0; j--) { Delete(id, version, j, obj, deleteStrings[j], session, loadedState); } }
/// <summary> /// Retrieve the cached database snapshot for the requested entity key. /// </summary> /// <param name="key">The entity key for which to retrieve the cached snapshot </param> /// <returns> The cached snapshot </returns> /// <remarks> /// <list type="bullet"> /// <listheader><description>This differs from <see cref="GetDatabaseSnapshot"/> is two important respects:</description></listheader> /// <item><description>no snapshot is obtained from the database if not already cached</description></item> /// <item><description>an entry of NO_ROW here is interpreted as an exception</description></item> /// </list> /// </remarks> public object[] GetCachedDatabaseSnapshot(EntityKey key) { object snapshot; if (!entitySnapshotsByKey.TryGetValue(key, out snapshot)) return null; if (snapshot == NoRow) { throw new HibernateException("persistence context reported no row snapshot for " + MessageHelper.InfoString(key.EntityName, key.Identifier)); } return (object[])snapshot; }
private IList DoQuery(ISessionImplementor session, QueryParameters queryParameters, bool returnProxies) { RowSelection selection = queryParameters.RowSelection; int maxRows = HasMaxRows(selection) ? selection.MaxRows : int.MaxValue; int entitySpan = EntityPersisters.Length; List<object> hydratedObjects = entitySpan == 0 ? null : new List<object>(entitySpan * 10); IDbCommand st = PrepareQueryCommand(queryParameters, false, session); IDataReader rs = GetResultSet(st, queryParameters.HasAutoDiscoverScalarTypes, queryParameters.Callable, selection, session); // would be great to move all this below here into another method that could also be used // from the new scrolling stuff. // // Would need to change the way the max-row stuff is handled (i.e. behind an interface) so // that I could do the control breaking at the means to know when to stop LockMode[] lockModeArray = GetLockModes(queryParameters.LockModes); EntityKey optionalObjectKey = GetOptionalObjectKey(queryParameters, session); bool createSubselects = IsSubselectLoadingEnabled; List<EntityKey[]> subselectResultKeys = createSubselects ? new List<EntityKey[]>() : null; IList results = new List<object>(); try { HandleEmptyCollections(queryParameters.CollectionKeys, rs, session); EntityKey[] keys = new EntityKey[entitySpan]; // we can reuse it each time if (log.IsDebugEnabled) { log.Debug("processing result set"); } int count; for (count = 0; count < maxRows && rs.Read(); count++) { if (log.IsDebugEnabled) { log.Debug("result set row: " + count); } object result = GetRowFromResultSet(rs, session, queryParameters, lockModeArray, optionalObjectKey, hydratedObjects, keys, returnProxies); results.Add(result); if (createSubselects) { subselectResultKeys.Add(keys); keys = new EntityKey[entitySpan]; //can't reuse in this case } } if (log.IsDebugEnabled) { log.Debug(string.Format("done processing result set ({0} rows)", count)); } } catch (Exception e) { e.Data["actual-sql-query"] = st.CommandText; throw; } finally { session.Batcher.CloseCommand(st, rs); } InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.ReadOnly); if (createSubselects) { CreateSubselects(subselectResultKeys, queryParameters, session); } return results; }
/// <summary> Add a canonical mapping from entity key to entity instance</summary> public void AddEntity(EntityKey key, object entity) { entitiesByKey[key] = entity; BatchFetchQueue.RemoveBatchLoadableEntityKey(key); }
/// <summary> /// Resolve any ids for currently loaded objects, duplications within the <c>IDataReader</c>, /// etc. Instanciate empty objects to be initialized from the <c>IDataReader</c>. Return an /// array of objects (a row of results) and an array of booleans (by side-effect) that determine /// wheter the corresponding object should be initialized /// </summary> private object[] GetRow(IDataReader rs, ILoadable[] persisters, EntityKey[] keys, object optionalObject, EntityKey optionalObjectKey, LockMode[] lockModes, IList hydratedObjects, ISessionImplementor session) { int cols = persisters.Length; IEntityAliases[] descriptors = EntityAliases; if (log.IsDebugEnabled) { log.Debug("result row: " + StringHelper.ToString(keys)); } object[] rowResults = new object[cols]; for (int i = 0; i < cols; i++) { object obj = null; EntityKey key = keys[i]; if (keys[i] == null) { // do nothing /* TODO NH-1001 : if (persisters[i]...EntityType) is an OneToMany or a ManyToOne and * the keys.length > 1 and the relation IsIgnoreNotFound probably we are in presence of * an load with "outer join" the relation can be considerer loaded even if the key is null (mean not found) */ } else { //If the object is already loaded, return the loaded one obj = session.GetEntityUsingInterceptor(key); if (obj != null) { //its already loaded so dont need to hydrate it InstanceAlreadyLoaded(rs, i, persisters[i], key, obj, lockModes[i], session); } else { obj = InstanceNotYetLoaded(rs, i, persisters[i], key, lockModes[i], descriptors[i].RowIdAlias, optionalObjectKey, optionalObject, hydratedObjects, session); } } rowResults[i] = obj; } return rowResults; }
/// <summary> /// Get the entity instance associated with the given <tt>EntityKey</tt> /// </summary> public object GetEntity(EntityKey key) { object result; entitiesByKey.TryGetValue(key, out result); return result; }
/// <summary> /// The entity instance is not in the session cache /// </summary> private object InstanceNotYetLoaded(IDataReader dr, int i, ILoadable persister, EntityKey key, LockMode lockMode, string rowIdAlias, EntityKey optionalObjectKey, object optionalObject, IList hydratedObjects, ISessionImplementor session) { object obj; string instanceClass = GetInstanceClass(dr, i, persister, key.Identifier, session); if (optionalObjectKey != null && key.Equals(optionalObjectKey)) { // its the given optional object obj = optionalObject; } else { obj = session.Instantiate(instanceClass, key.Identifier); } // need to hydrate it // grab its state from the DataReader and keep it in the Session // (but don't yet initialize the object itself) // note that we acquired LockMode.READ even if it was not requested LockMode acquiredLockMode = lockMode == LockMode.None ? LockMode.Read : lockMode; LoadFromResultSet(dr, i, obj, instanceClass, key, rowIdAlias, acquiredLockMode, persister, session); // materialize associations (and initialize the object) later hydratedObjects.Add(obj); return obj; }
/// <summary> Is there an entity with the given key in the persistence context</summary> public bool ContainsEntity(EntityKey key) { return entitiesByKey.ContainsKey(key); }
/// <summary> /// Return the Underlying Persistent Object in a given <see cref="ISession"/>, or null. /// </summary> /// <param name="s">The Session to get the object from.</param> /// <returns>The Persistent Object this proxy is Proxying, or <see langword="null" />.</returns> public object GetImplementation(ISessionImplementor s) { EntityKey key = new EntityKey(Identifier, s.Factory.GetEntityPersister(EntityName), s.EntityMode); return s.PersistenceContext.GetEntity(key); }
/// <summary> /// Remove an entity from the session cache, also clear /// up other state associated with the entity, all except /// for the <tt>EntityEntry</tt> /// </summary> public object RemoveEntity(EntityKey key) { object tempObject = entitiesByKey[key]; entitiesByKey.Remove(key); object entity = tempObject; List<EntityUniqueKey> toRemove = new List<EntityUniqueKey>(); foreach (KeyValuePair<EntityUniqueKey, object> pair in entitiesByUniqueKey) { if (pair.Value == entity) toRemove.Add(pair.Key); } foreach (EntityUniqueKey uniqueKey in toRemove) { entitiesByUniqueKey.Remove(uniqueKey); } entitySnapshotsByKey.Remove(key); nullifiableEntityKeys.Remove(key); BatchFetchQueue.RemoveBatchLoadableEntityKey(key); BatchFetchQueue.RemoveSubselect(key); return entity; }