private void DoUpdateMutable( object obj, object id, IClassPersister persister ) { if( log.IsDebugEnabled ) { log.Debug( "updating " + MessageHelper.InfoString( persister, id ) ); } Key key = new Key( id, persister ); CheckUniqueness( key, obj ); if( persister.ImplementsLifecycle ) { log.Debug( "calling onUpdate()" ); if( ( ( ILifecycle ) obj ).OnUpdate( this ) == LifecycleVeto.Veto ) { log.Debug( "update vetoed by onUpdate()" ); Reassociate( obj, id, persister ); return; } } // this is a transient object with existing persistent state not loaded by the session new OnUpdateVisitor( this, id ).Process( obj, persister ); AddEntity( key, obj ); AddEntry( obj, Status.Loaded, null, id, persister.GetVersion( obj ), LockMode.None, true, persister, false ); }
/// <summary> /// The entity instance is not in the session cache /// </summary> /// <param name="dr"></param> /// <param name="i"></param> /// <param name="persister"></param> /// <param name="suffix"></param> /// <param name="key"></param> /// <param name="lockMode"></param> /// <param name="optionalObjectKey"></param> /// <param name="optionalObject"></param> /// <param name="hydratedObjects"></param> /// <param name="session"></param> /// <returns></returns> private object InstanceNotYetLoaded( IDataReader dr, int i, ILoadable persister, string suffix, Key key, LockMode lockMode, Key optionalObjectKey, object optionalObject, IList hydratedObjects, ISessionImplementor session ) { object obj; System.Type 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, key, suffix, acquiredLockMode, persister, session ); // materialize associations (and initialize the object) later hydratedObjects.Add( obj ); return obj; }
/// <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> /// <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 ) { Key key = new Key( _id, _session.Factory.GetPersister( _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; }
private IList DoQuery( ISessionImplementor session, QueryParameters queryParameters, object optionalObject, object optionalId, object[ ] optionalCollectionKeys, bool returnProxies ) { RowSelection selection = queryParameters.RowSelection; int maxRows = HasMaxRows( selection ) ? selection.MaxRows : int.MaxValue; ILoadable[ ] persisters = Persisters; int cols = persisters.Length; ArrayList hydratedObjects = cols > 0 ? new ArrayList() : null; IList results = new ArrayList(); IDbCommand st = PrepareQueryCommand( ApplyLocks( SqlString, queryParameters.LockModes, session.Factory.Dialect ), queryParameters, false, session ); IDataReader rs = GetResultSet( st, selection, session ); try { if( optionalCollectionKeys != null ) { HandleEmptyCollections( optionalCollectionKeys, rs, session ); } Key[ ] keys = new Key[cols]; // we can reuse it each time if( log.IsDebugEnabled ) { log.Debug( "processing result set" ); } int count; for( count = 0; count < maxRows && rs.Read(); count++ ) { object result = GetRowFromResultSet( rs, session, queryParameters, hydratedObjects, optionalObject, optionalId, keys, returnProxies ); results.Add( result ); } if( log.IsDebugEnabled ) { log.Debug( string.Format( "done processing result set ({0} rows)", count ) ); } } catch( Exception sqle ) { ADOExceptionReporter.LogExceptions( sqle ); throw; } finally { session.Batcher.CloseQueryCommand( st, rs ); } InitializeEntitiesAndCollections( hydratedObjects, rs, session ); return results; // GetResultList( results ); }
/// <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> /// <param name="rs"></param> /// <param name="persisters"></param> /// <param name="suffixes"></param> /// <param name="keys"></param> /// <param name="optionalObject"></param> /// <param name="optionalObjectKey"></param> /// <param name="session"></param> /// <param name="hydratedObjects"></param> /// <param name="lockModes"></param> /// <returns></returns> private object[ ] GetRow( IDataReader rs, ILoadable[ ] persisters, string[ ] suffixes, Key[ ] keys, object optionalObject, Key optionalObjectKey, LockMode[ ] lockModes, IList hydratedObjects, ISessionImplementor session ) { int cols = persisters.Length; 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; Key key = keys[ i ]; if( keys[ i ] == null ) { // do nothing } else { //If the object is already loaded, return the loaded one obj = session.GetEntity( key ); if( obj != null ) { //its already loaded so dont need to hydrate it InstanceAlreadyLoaded( rs, i, persisters[ i ], suffixes[ i ], key, obj, lockModes[ i ], session ); } else { obj = InstanceNotYetLoaded( rs, i, persisters[ i ], suffixes[ i ], key, lockModes[ i ], optionalObjectKey, optionalObject, hydratedObjects, session ); } } rowResults[ i ] = obj; } return rowResults; }
private object DoSave( object obj, object id, IClassPersister persister, bool useIdentityColumn, Cascades.CascadingAction cascadeAction, object anything ) { if( log.IsDebugEnabled ) { log.Debug( "saving " + MessageHelper.InfoString( persister, id ) ); } Key key; if( useIdentityColumn ) { // if the id is generated by the database, we assign the key later key = null; } else { key = new Key( id, persister ); object old = GetEntity( key ); if( old != null ) { EntityEntry e = GetEntry( old ); if( e.Status == Status.Deleted ) { ForceFlush( e ); } else { throw new NonUniqueObjectException( id, persister.MappedClass ); } } persister.SetIdentifier( obj, id ); } // Sub-insertions should occur before containing insertsion so // Try to do the callback not if( persister.ImplementsLifecycle ) { log.Debug( "calling OnSave()" ); if( ( ( ILifecycle ) obj ).OnSave( this ) == LifecycleVeto.Veto ) { log.Debug( "insertion vetoed by OnSave()" ); return id; } } return DoSave( obj, key, persister, false, useIdentityColumn, cascadeAction, anything ); }
public void CreateWithWrongTypeOfId() { IClassPersister persister = new TestingClassPersister(); Key key = new Key(1L, persister); }
/// <summary> /// 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> /// <param name="key"></param> /// <param name="obj"></param> /// <param name="lockMode"></param> public void AddUninitializedEntity( Key key, object obj, LockMode lockMode ) { AddEntity( key, obj ); AddEntry( obj, Status.Loading, null, key.Identifier, null, lockMode, true, null, false ); //temporary }
/// <summary> /// Load the data for the object with the specified id into a newly created /// object. A new key will be assigned to the object. If the class supports /// lazy initialization, return a proxy instead, leaving the real work for /// later. This should return an existing proxy where appropriate. /// </summary> /// <param name="clazz">The <see cref="System.Type"/> of the object to load.</param> /// <param name="id">The identifier of the object in the database.</param> /// <param name="checkDeleted"> /// A boolean indicating if NHiberate should check if the object has or has not been deleted. /// </param> /// <param name="allowProxyCreation">A boolean indicating if it is allowed to return a Proxy instead of an instance of the <see cref="System.Type"/>.</param> /// <returns> /// An loaded instance of the object or a proxy of the object is proxies are allowed. /// </returns> /// <remarks> /// If the parameter <c>checkDeleted</c> is <c>false</c> it is possible to return an object that has /// been deleted by the user in this <see cref="ISession"/>. If the parameter <c>checkDeleted</c> is /// <c>true</c> and the object has been deleted then an <see cref="ObjectDeletedException"/> will be /// thrown. /// </remarks> private object DoLoadByClass( System.Type clazz, object id, bool checkDeleted, bool allowProxyCreation ) { if( log.IsDebugEnabled ) { log.Debug( "loading " + MessageHelper.InfoString( clazz, id ) ); } IClassPersister persister = GetClassPersister( clazz ); if( !persister.HasProxy ) { // this class has no proxies (so do a shortcut) return DoLoad( clazz, id, null, LockMode.None, checkDeleted ); } else { Key key = new Key( id, persister ); object proxy = null; if( GetEntity( key ) != null ) { // return existing object or initialized proxy (unless deleted) return ProxyFor( persister, key, DoLoad( clazz, id, null, LockMode.None, checkDeleted ) ); } else if( ( proxy = proxiesByKey[ key ] ) != null ) { // return existing uninitizlied proxy return NarrowProxy( proxy, persister, key, null ); } else if( allowProxyCreation ) { // return new uninitailzed proxy proxy = persister.CreateProxy( id, this ); if( persister.IsBatchLoadable ) { batchLoadableEntityKeys[ key ] = Marker; } proxiesByKey[ key ] = proxy; return proxy; } else { // return a newly loaded object return DoLoad( clazz, id, null, LockMode.None, checkDeleted ); } } }
/// <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. /// </summary> /// <param name="proxy"></param> /// <param name="persister"></param> /// <param name="key"></param> /// <param name="obj"></param> /// <returns></returns> public object NarrowProxy( object proxy, IClassPersister persister, Key key, object obj ) { if( !persister.ConcreteProxyClass.IsAssignableFrom( proxy.GetType() ) ) { if( log.IsWarnEnabled ) { log.Warn( "Narrowing proxy to " + persister.ConcreteProxyClass + " - this operation breaks ==" ); } if( obj != null ) { proxiesByKey.Remove( key ); return obj; } else { proxy = persister.CreateProxy( key.Identifier, this ); proxiesByKey[ key ] = proxy; return proxy; } } else { return proxy; } }
/// <summary> /// Grab the existing proxy for an instance, if one exists. /// (otherwise return the instance) /// </summary> /// <param name="persister"></param> /// <param name="key"></param> /// <param name="impl"></param> /// <returns></returns> public object ProxyFor( IClassPersister persister, Key key, object impl ) { if( !persister.HasProxy || key == null ) { return impl; } object proxy = proxiesByKey[ key ]; if( proxy != null ) { return NarrowProxy( proxy, persister, key, impl ); } else { return impl; } }
private EntityEntry Reassociate( object obj, object id, IClassPersister persister ) { if( log.IsDebugEnabled ) { log.Debug( "reassociating transient instance: " + MessageHelper.InfoString( persister, id ) ); } Key key = new Key( id, persister ); CheckUniqueness( key, obj ); AddEntity( key, obj ); Object[ ] values = persister.GetPropertyValues( obj ); TypeFactory.DeepCopy( values, persister.PropertyTypes, persister.PropertyUpdateability, values ); object version = Versioning.GetVersion( values, persister ); EntityEntry newEntry = AddEntry( obj, Status.Loaded, values, id, version, LockMode.None, true, persister, false ); new OnLockVisitor( this, id ).Process( obj, persister ); return newEntry; }
private void CheckUniqueness( Key key, object obj ) { object entity = GetEntity( key ); if( entity == obj ) { throw new AssertionFailure( "object already associated in DoSave()" ); } if( entity != null ) { throw new NonUniqueObjectException( key.Identifier, key.MappedClass ); } }
/// <summary> /// Used only by Replicate /// </summary> /// <param name="obj"></param> /// <param name="id"></param> /// <param name="version"></param> /// <param name="replicationMode"></param> /// <param name="persister"></param> private void DoReplicate( object obj, object id, object version, ReplicationMode replicationMode, IClassPersister persister ) { if( log.IsDebugEnabled ) { log.Debug( "replicating changes to " + MessageHelper.InfoString( persister, id ) ); } new OnReplicateVisitor( this, id ).Process( obj, persister ); Key key = new Key( id, persister ); AddEntity( key, obj ); AddEntry( obj, Status.Loaded, null, id, version, LockMode.None, true, persister, true ); cascading++; try { // do cascade Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionReplicate, CascadePoint.CascadeOnUpdate, replicationMode ); } finally { cascading--; } }
/// <summary> /// /// </summary> /// <param name="key"></param> /// <returns></returns> public object GetEntity( Key key ) { return entitiesByKey[ key ]; }
/// <summary> /// Actually do all the hard work of loading up an object /// </summary> /// <param name="theClass"></param> /// <param name="id"></param> /// <param name="optionalObject"></param> /// <param name="lockMode"></param> /// <param name="checkDeleted"></param> /// <returns></returns> /// <remarks> /// 1. see if it is already loaded /// 2. see if it is cached /// 3. actually go to the database /// </remarks> private object DoLoad( System.Type theClass, object id, object optionalObject, LockMode lockMode, bool checkDeleted ) { //DONT need to flush before a load by id, because ids are constant if( log.IsDebugEnabled ) { log.Debug( "attempting to resolve " + MessageHelper.InfoString( theClass, id ) ); } IClassPersister persister = GetClassPersister( theClass ); Key key = new Key( id, persister ); if( optionalObject != null ) { persister.SetIdentifier( optionalObject, id ); } // LOOK FOR LOADED OBJECT // Look for Status.Loaded object object old = GetEntity( key ); if( old != null ) { //if this object was already loaded EntityEntry oldEntry = GetEntry( old ); Status status = oldEntry.Status; if( checkDeleted && ( status == Status.Deleted || status == Status.Gone ) ) { throw new ObjectDeletedException( "The object with that id was deleted", id, theClass ); } UpgradeLock( old, oldEntry, lockMode ); if( log.IsDebugEnabled ) { log.Debug( "resolved object in session cache " + MessageHelper.InfoString( persister, id ) ); } return old; } else { // check to see if we know already that it does not exist: if( nonExists.Contains( key ) ) { log.Debug( "entity does not exist" ); return null; } // LOOK IN CACHE CacheEntry entry = persister.HasCache && lockMode.LessThan( LockMode.Read ) ? ( CacheEntry ) persister.Cache.Get( id, Timestamp ) : null; if( entry != null ) { return AssembleCacheEntry( entry, id, persister, optionalObject ); } else { //GO TO DATABASE if( log.IsDebugEnabled ) { log.Debug( "object not resolved in any cache " + MessageHelper.InfoString( persister, id ) ); } object result = persister.Load( id, optionalObject, lockMode, this ); if( result == null ) { // remember it doesn't exist, in case of next time AddNonExist( key ); } return result; } } }
private object RemoveEntity( Key key ) { object retVal = entitiesByKey[ key ]; entitiesByKey.Remove( key ); return retVal; }
public void Refresh( object obj, LockMode lockMode ) { CheckIsOpen(); if( obj == null ) { throw new ArgumentNullException( "obj", "attempted to refresh null" ); } if( ReassociateIfUninitializedProxy( obj ) ) { return; } object theObj = UnproxyAndReassociate( obj ); EntityEntry e = RemoveEntry( theObj ); IClassPersister persister; object id; if( e == null ) { persister = GetPersister( theObj ); id = persister.GetIdentifier( theObj ); if( log.IsDebugEnabled ) { log.Debug( "refreshing transient " + MessageHelper.InfoString( persister, id ) ); } if( GetEntry( new Key( id, persister ) ) != null ) { throw new PersistentObjectException( "attempted to refresh transient instance when persistent instance was already associated with the Session: " + MessageHelper.InfoString( persister, id ) ); } } else { if( log.IsDebugEnabled ) { log.Debug( "refreshing " + MessageHelper.InfoString( e.Persister, e.Id ) ); } if( !e.ExistsInDatabase ) { throw new HibernateException( "this instance does not yet exist as a row in the database" ); } persister = e.Persister; id = e.Id; Key key = new Key( id, persister ); RemoveEntity( key ); if( persister.HasCollections ) { new EvictVisitor( this ).Process( obj, persister ); } } if( persister.HasCache ) { persister.Cache.Remove( id ); } EvictCachedCollections( persister, id ); object result = persister.Load( id, theObj, lockMode, this ); UnresolvableObjectException.ThrowIfNull( result, id, persister.MappedClass ); }
private object DoSave( object theObj, Key key, IClassPersister persister, bool replicate, bool useIdentityColumn, Cascades.CascadingAction cascadeAction, object anything ) { if( persister.ImplementsValidatable ) { ( ( IValidatable ) theObj ).Validate(); } object id; if( useIdentityColumn ) { id = null; ExecuteInserts(); } else { id = key.Identifier; } // Put a placeholder in entries, so we don't recurse back to try and Save() the // same object again. QUESTION: Should this be done before OnSave() is called? // likewise, should it be done before OnUpdate()? AddEntry( theObj, Status.Saving, null, id, null, LockMode.Write, useIdentityColumn, persister, false ); // okay if id is null here // cascade-save to many-to-one BEFORE the parent is saved cascading++; try { Cascades.Cascade( this, persister, theObj, cascadeAction, CascadePoint.CascadeBeforeInsertAfterDelete, anything ); } finally { cascading--; } object[ ] values = persister.GetPropertyValues( theObj ); IType[ ] types = persister.PropertyTypes; bool substitute = false; if( !replicate ) { substitute = interceptor.OnSave( theObj, id, values, persister.PropertyNames, types ); // Keep the existing version number in the case of replicate! if( persister.IsVersioned ) { // IsUnsavedVersion bit below is NHibernate-specific substitute = Versioning.SeedVersion( values, persister.VersionProperty, persister.VersionType, persister.IsUnsavedVersion( values ) ) || substitute; } } if( persister.HasCollections ) { // TODO: make OnReplicateVisitor extend WrapVisitor if( replicate ) { OnReplicateVisitor visitor = new OnReplicateVisitor( this, id ); visitor.ProcessValues( values, types ); } WrapVisitor visitor2 = new WrapVisitor( this ); // substitutes into values by side-effect visitor2.ProcessValues( values, types ); substitute = substitute || visitor2.IsSubstitutionRequired; } if( substitute ) { persister.SetPropertyValues( theObj, values ); } TypeFactory.DeepCopy( values, types, persister.PropertyUpdateability, values ); NullifyTransientReferences( values, types, useIdentityColumn, theObj ); CheckNullability( values, persister, false ); if( useIdentityColumn ) { ScheduledIdentityInsertion insert = new ScheduledIdentityInsertion( values, theObj, persister, this ); Execute( insert ); id = insert.GeneratedId; persister.SetIdentifier( theObj, id ); key = new Key( id, persister ); CheckUniqueness( key, theObj ); } object version = Versioning.GetVersion( values, persister ); AddEntity( key, theObj ); AddEntry( theObj, Status.Loaded, values, id, version, LockMode.Write, useIdentityColumn, persister, replicate ); nonExists.Remove( key ); if( !useIdentityColumn ) { insertions.Add( new ScheduledInsertion( id, values, theObj, version, persister, this ) ); } // cascade-save to collections AFTER the collection owner was saved cascading++; try { Cascades.Cascade( this, persister, theObj, cascadeAction, CascadePoint.CascadeAfterInsertBeforeDelete, anything ); } finally { cascading--; } return id; }
public void PostDelete( object obj ) { EntityEntry entry = RemoveEntry( obj ); if( entry == null ) { throw new AssertionFailure( "possible nonthreadsafe access to session" ); } entry.Status = Status.Gone; entry.ExistsInDatabase = false; Key key = new Key( entry.Id, entry.Persister ); RemoveEntity( key ); proxiesByKey.Remove( key ); }
private object GetRowFromResultSet( IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, IList hydratedObjects, object optionalObject, object optionalId, Key[ ] keys, bool returnProxies ) { ILoadable[ ] persisters = Persisters; int cols = persisters.Length; ICollectionPersister collectionPersister = CollectionPersister; int collectionOwner = CollectionOwner; string[ ] suffixes = Suffixes; LockMode[ ] lockModeArray = GetLockModes( queryParameters.LockModes ); int[ ] owners = Owners; // this is a CollectionInitializer and we are loading up a single collection bool hasCollections = collectionPersister != null; // this is a query and we are loading multiple instances of the same collection role bool hasCollectionOwners = hasCollections && collectionOwner >= 0; Key optionalObjectKey; if( optionalObject != null ) { optionalObjectKey = new Key( optionalId, session.GetPersister( optionalObject ) ); } else { optionalObjectKey = null; } for( int i = 0; i < cols; i++ ) { keys[ i ] = GetKeyFromResultSet( i, Persisters[ i ], ( i == cols - 1 ) ? optionalId : null, resultSet, session ); //TODO: the i==cols-1 bit depends upon subclass implementation (very bad) } if( owners != null ) { RegisterNonExists( keys, owners, persisters, session ); } // this call is side-effecty object[ ] row = GetRow( resultSet, persisters, suffixes, keys, optionalObject, optionalObjectKey, lockModeArray, hydratedObjects, session ); if( returnProxies ) { for( int i = 0; i < cols; i++ ) { // now get an existing proxy for each row element (if there is one) row[ i ] = session.ProxyFor( persisters[ i ], keys[ i ], row[ i ] ); } } if( hasCollections ) { //if null, owner will be retrieved from session object owner = hasCollectionOwners ? row[ collectionOwner ] : null; object key = owner != null ? keys[ collectionOwner ].Identifier : null; ReadCollectionElement( owner, key, resultSet, session ); } return GetResultColumnOrRow( row, resultSet, session ); }
/// <summary> /// record the fact that this collection was dereferenced /// </summary> /// <param name="coll"></param> private void UpdateUnreachableCollection( PersistentCollection coll ) { CollectionEntry entry = GetCollectionEntry( coll ); if( log.IsDebugEnabled && entry.loadedPersister != null ) { log.Debug( "collection dereferenced: " + MessageHelper.InfoString( entry.loadedPersister, entry.loadedKey ) ); } // do a check if( entry.loadedPersister != null && entry.loadedPersister.HasOrphanDelete ) { Key key = new Key( entry.loadedKey, GetClassPersister( entry.loadedPersister.OwnerClass ) ); object owner = GetEntity( key ); if( owner == null ) { throw new AssertionFailure( "owner not associated with session" ); } EntityEntry e = 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( "You may not dereference an collection with cascade=\"all-delete-orphan\"" ); } } // do the work entry.currentPersister = null; entry.currentKey = null; PrepareCollectionForUpdate( coll, entry ); }
private void RegisterNonExists( Key[ ] keys, int[ ] owners, ILoadable[ ] persisters, ISessionImplementor session ) { for( int i = 0; i < keys.Length; i++ ) { int owner = owners[ i ]; if( owner > -1 ) { Key ownerKey = keys[ owner ]; if( keys[ i ] == null && ownerKey != null ) { session.AddNonExist( new Key( ownerKey.Identifier, persisters[ i ] ) ); } } } }
/// <summary> /// remove any hard references to the entity that are held by the infrastructure /// (references held by application or other persistant instances are okay) /// </summary> /// <param name="obj"></param> public void Evict( object obj ) { CheckIsOpen(); if( obj is INHibernateProxy ) { LazyInitializer li = NHibernateProxyHelper.GetLazyInitializer( ( INHibernateProxy ) obj ); object id = li.Identifier; IClassPersister persister = GetClassPersister( li.PersistentClass ); Key key = new Key( id, persister ); proxiesByKey.Remove( key ); if( !li.IsUninitialized ) { object entity = RemoveEntity( key ); if( entity != null ) { EntityEntry e = RemoveEntry( entity ); DoEvict( e.Persister, entity ); } } } else { EntityEntry e = RemoveEntry( obj ); if( e != null ) { RemoveEntity( new Key( e.Id, e.Persister ) ); DoEvict( e.Persister, obj ); } } }
/// <summary> /// The entity instance is already in the session cache /// </summary> private void InstanceAlreadyLoaded( IDataReader rs, int i, ILoadable persister, string suffix, Key key, object obj, LockMode lockMode, ISessionImplementor session ) { if( !persister.MappedClass.IsAssignableFrom( obj.GetType() ) ) { throw new WrongClassException( "loading object was of wrong class", key.Identifier, persister.MappedClass ); } if( LockMode.None != lockMode && UpgradeLocks() ) { // 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( persister.IsVersioned && session.GetLockMode( obj ).LessThan( lockMode ) ) { // we only check the version when _upgrading_ lock modes CheckVersion( i, persister, key.Identifier, session.GetVersion( obj ), rs, session ); // we need to upgrade the lock mode to the mode requested session.SetLockMode( obj, lockMode ); } } }
public void AddNonExist( Key key ) { nonExists.Add( key ); }
/// <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> /// <param name="rs"></param> /// <param name="i"></param> /// <param name="obj"></param> /// <param name="key"></param> /// <param name="suffix"></param> /// <param name="lockMode"></param> /// <param name="rootPersister"></param> /// <param name="session"></param> private void LoadFromResultSet( IDataReader rs, int i, object obj, Key key, string suffix, LockMode lockMode, ILoadable rootPersister, ISessionImplementor session ) { if( log.IsDebugEnabled ) { log.Debug( "Initializing object from DataReader: " + key ); } // 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) session.AddUninitializedEntity( key, obj, lockMode ); // Get the persister for the _subclass_ ILoadable persister = ( ILoadable ) session.GetPersister( obj ); // This is not very nice (and quite slow): string[ ][ ] cols = persister == rootPersister ? suffixedPropertyColumns[ i ] : GetSuffixedPropertyAliases( persister, suffix ); object id = key.Identifier; object[ ] values = Hydrate( rs, id, obj, persister, session, cols ); session.PostHydrate( persister, id, values, obj, lockMode ); }
private void AddEntity( Key key, object obj ) { entitiesByKey[ key ] = obj; if( key.IsBatchLoadable ) { batchLoadableEntityKeys.Remove( 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 <c>null</c>.</returns> public object GetImplementation( ISessionImplementor s ) { Key key = new Key( Identifier, s.Factory.GetPersister( PersistentClass ) ); return s.GetEntity( key ); }
private void DoDelete( object obj, EntityEntry entry, IClassPersister persister ) { if( log.IsDebugEnabled ) { log.Debug( "deleting " + MessageHelper.InfoString( persister, entry.Id ) ); } IType[ ] propTypes = persister.PropertyTypes; object version = entry.Version; object[ ] loadedState; if( entry.LoadedState == null ) { //ie the object came in from Update() loadedState = persister.GetPropertyValues( obj ); } else { loadedState = entry.LoadedState; } entry.DeletedState = new object[loadedState.Length]; TypeFactory.DeepCopy( loadedState, propTypes, persister.PropertyUpdateability, entry.DeletedState ); interceptor.OnDelete( obj, entry.Id, entry.DeletedState, persister.PropertyNames, propTypes ); entry.Status = Status.Deleted; // before cascade and Lifecycle callback, so we are circular-reference safe Key key = new Key( entry.Id, persister ); IList deletionsByOnDelete = null; ISet nullifiablesAfterOnDelete = null; // do Lifecycle callback before cascades, since this can veto if( persister.ImplementsLifecycle ) { ISet oldNullifiables = ( ISet ) nullifiables.Clone(); ArrayList oldDeletions = ( ArrayList ) deletions.Clone(); nullifiables.Add( key ); //the deletion of the parent is actually executed BEFORE any deletion from onDelete() try { log.Debug( "calling onDelete()" ); if( ( ( ILifecycle ) obj ).OnDelete( this ) == LifecycleVeto.Veto ) { //rollback deletion entry.Status = Status.Loaded; entry.DeletedState = null; nullifiables = oldNullifiables; log.Debug( "deletion vetoed by OnDelete()" ); return; // don't let it cascade } } catch( Exception ) { //rollback deletion entry.Status = Status.Loaded; entry.DeletedState = null; nullifiables = oldNullifiables; throw; } //note, the following assumes that onDelete() didn't cause the session to be flushed! // TODO: add a better check that it doesn't if( oldDeletions.Count > deletions.Count ) { throw new HibernateException( "session was flushed during onDelete()" ); } deletionsByOnDelete = Sublist( deletions, oldDeletions.Count, deletions.Count ); deletions = oldDeletions; nullifiablesAfterOnDelete = nullifiables; nullifiables = oldNullifiables; } cascading++; try { // cascade-delete to collections "BEFORE" the collection owner is deleted Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionDelete, CascadePoint.CascadeAfterInsertBeforeDelete, null ); } finally { cascading--; } NullifyTransientReferences( entry.DeletedState, propTypes, false, obj ); // NH: commented out, seems illogical to check for nulls here //CheckNullability( entry.DeletedState, persister, true ); nullifiables.Add( key ); ScheduledDeletion delete = new ScheduledDeletion( entry.Id, version, obj, persister, this ); deletions.Add( delete ); // Ensures that containing deletions happen before sub-deletions if( persister.ImplementsLifecycle ) { // after nullify, because we don't want to nullify references to subdeletions nullifiables.AddAll( nullifiablesAfterOnDelete ); // after deletions.add(), to respect foreign key constraints deletions.AddRange( deletionsByOnDelete ); } cascading++; try { // cascade-delete to many-to-one AFTER the parent was deleted Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionDelete, CascadePoint.CascadeBeforeInsertAfterDelete ); } finally { cascading--; } }