From: Michael D. <mik...@us...> - 2005-02-07 01:34:52
|
Update of /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10106/Impl Modified Files: AbstractVisitor.cs CollectionKey.cs FlushVisitor.cs OnLockVisitor.cs OnUpdateVisitor.cs ProxyVisitor.cs ReattachVisitor.cs SessionFactoryImpl.cs SessionImpl.cs WrapVisitor.cs Added Files: DirtyCollectionSearchVisitor.cs EvictVisitor.cs Log Message: modifications to Collections and their Types for caching related code. Formatting of the Visitor classes and added in two missing Visitors. Index: SessionImpl.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/SessionImpl.cs,v retrieving revision 1.64 retrieving revision 1.65 diff -C2 -d -r1.64 -r1.65 *** SessionImpl.cs 6 Feb 2005 01:59:02 -0000 1.64 --- SessionImpl.cs 7 Feb 2005 01:34:41 -0000 1.65 *************** *** 821,824 **** --- 821,826 ---- if ( persister.HasCollections ) { + // h2.1 has some extra code here for OnReplicateVisitor - is a new setting + // that is only in h2.1 because of the method Replicate(object, ReplicateMode) WrapVisitor visitor = new WrapVisitor(this); // substitutes into values by side-effect *************** *** 879,885 **** } internal bool ReassociateIfUninitializedProxy(object value) { ! if (!NHibernateUtil.IsInitialized(value)) { ReassociateProxy(value); --- 881,895 ---- } + /// <summary> + /// If the parameter <c>value</c> is an unitialized proxy then it will be reassociated + /// with the session. + /// </summary> + /// <param name="value">A persistable object, proxy, persistent collection or null</param> + /// <returns> + /// <c>true</c> when an uninitialized proxy was passed into this method, <c>false</c> otherwise. + /// </returns> internal bool ReassociateIfUninitializedProxy(object value) { ! if( !NHibernateUtil.IsInitialized(value) ) { ReassociateProxy(value); *************** *** 1110,1114 **** new OnUpdateVisitor(this, id).Process( obj, persister ); - //RemoveCollectionsFor( persister, id, obj ); AddEntity( new Key( id, persister ), obj ); --- 1120,1123 ---- *************** *** 1333,1339 **** } ! if( !NHibernateUtil.IsInitialized( obj ) ) { - ReassociateProxy( obj ); return; } --- 1342,1347 ---- } ! if( ReassociateIfUninitializedProxy( obj ) ) { return; } *************** *** 1376,1384 **** } ! if( !NHibernateUtil.IsInitialized( obj ) ) { - ReassociateProxy( obj ); return; } object theObj = UnproxyAndReassociate( obj ); --- 1384,1392 ---- } ! if( ReassociateIfUninitializedProxy( obj ) ) { return; } + object theObj = UnproxyAndReassociate( obj ); *************** *** 1472,1478 **** } ! if( !NHibernateUtil.IsInitialized( obj ) ) { - ReassociateProxy( obj ); return; } --- 1480,1485 ---- } ! if( ReassociateIfUninitializedProxy( obj ) ) { return; } *************** *** 2501,2507 **** } ! if( !NHibernateUtil.IsInitialized( obj ) ) { - ReassociateProxy( obj ); return; } --- 2508,2513 ---- } ! if( ReassociateIfUninitializedProxy( obj ) ) { return; } *************** *** 2509,2517 **** object theObj = UnproxyAndReassociate( obj ); EntityEntry e = RemoveEntry( theObj ); ! if( e == null ) { ! IClassPersister persister = GetPersister( theObj ); ! object id = persister.GetIdentifier( theObj ); if( log.IsDebugEnabled ) { --- 2515,2525 ---- 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 ) { *************** *** 2519,2522 **** --- 2527,2533 ---- } + // TODO: add another check here about refreshing transient instance when persisted + // instance already associated with the session. + DoLoadByObject( theObj, id, true, lockMode ); } *************** *** 2533,2540 **** } ! Key key = new Key( e.Id, e.Persister ); RemoveEntity( key ); ! EvictCollections( e.Persister.GetPropertyValues( obj ), e.Persister.PropertyTypes ); try { --- 2544,2565 ---- } ! persister = e.Persister; ! id = e.Id; ! Key key = new Key( id, persister ); RemoveEntity( key ); ! if( persister.HasCollections ) ! { ! new EvictVisitor( this ).Process( obj, persister ); ! } + // this is from h2.1 - the code around here has some more interaction with the factory + // and collection cache. + if( persister.HasCache ) + { + persister.Cache.Remove( id ); + } + EvictCachedCollections( persister, id ); + + // h2.1 has some differences with how it interacts with Load and errors thrown. try { *************** *** 2914,2923 **** // compare to cached state (ignoring nested collections) ! if( persister.IsMutable && ! ( cannotDirtyCheck || ! ( dirtyProperties != null && dirtyProperties.Length != 0 ) || ! ( status == Status.Loaded && persister.IsVersioned && persister.HasCollections && SearchForDirtyCollections( values, types ) ) ! ) ! ) { // its dirty! --- 2939,2943 ---- // compare to cached state (ignoring nested collections) ! if ( IsUpdateNecessary(persister, cannotDirtyCheck, status, dirtyProperties, values, types) ) { // its dirty! *************** *** 2995,2998 **** --- 3015,3038 ---- } + private bool IsUpdateNecessary( IClassPersister persister, bool cannotDirtyCheck, Status status, int[] dirtyProperties, + object[] values, IType[] types) + { + if( persister.IsMutable==false ) return false; + if( cannotDirtyCheck ) return true; + + if( dirtyProperties!=null && dirtyProperties.Length!=0 ) return true; + + if( status==Status.Loaded && persister.IsVersioned && persister.HasCollections ) + { + DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor( this ); + visitor.ProcessValues( values, types ); + return visitor.WasDirtyCollectionFound; + } + else + { + return false; + } + } + /// <summary> /// Process cascade save/update at the start of a flush to discover *************** *** 3412,3416 **** /// <param name="coll"></param> /// <returns></returns> ! private bool CollectionIsDirty( PersistentCollection coll ) { CollectionEntry entry = GetCollectionEntry( coll ); --- 3452,3456 ---- /// <param name="coll"></param> /// <returns></returns> ! internal bool CollectionIsDirty( PersistentCollection coll ) { CollectionEntry entry = GetCollectionEntry( coll ); *************** *** 3418,3489 **** } - /// <summary> - /// Given an array of fields, search recursively for dirty collections. - /// </summary> - /// <param name="fields"></param> - /// <param name="types"></param> - /// <returns>return true if we find one</returns> - private bool SearchForDirtyCollections( object[ ] fields, IType[ ] types ) - { - for( int i = 0; i < types.Length; i++ ) - { - if( SearchForDirtyCollections( fields[ i ], types[ i ] ) ) - { - return true; - } - } - return false; - } - - /// <summary> - /// Do we have a dirty collection here? - /// 1. if it is a new application-instantiated collection, return true (does not occur anymore!) - /// 2. if it is a component, recurse - /// 3. if it is a wrappered collection, ask the collection entry - /// </summary> - /// <param name="obj"></param> - /// <param name="type"></param> - /// <returns></returns> - private bool SearchForDirtyCollections( object obj, IType type ) - { - if( obj != null ) - { - if( type.IsPersistentCollectionType ) - { - if( obj.GetType().IsArray ) - { - PersistentCollection ah = GetArrayHolder( obj ); - // if no array holder we found an unwrappered array (this can't occur, - // because we now always call wrap() before getting to here) - //return (ah==null) ? true : SearchForDirtyCollections(ah, type); - return CollectionIsDirty( ah ); - } - else - { - // if not wrappered yet, its dirty (this can't occur, because - // we now always call wrap() before getting to here) - // return ( ! (obj is PersistentCollection) ) ? - // true : SearchForDirtyCollections( (PersistentCollection) obj, type ); - return CollectionIsDirty( ( PersistentCollection ) obj ); - } - } - - else if( type.IsComponentType ) - { - IAbstractComponentType componentType = ( IAbstractComponentType ) type; - object[ ] values = componentType.GetPropertyValues( obj, this ); - IType[ ] types = componentType.Subtypes; - for( int i = 0; i < values.Length; i++ ) - { - if( SearchForDirtyCollections( values[ i ], types[ i ] ) ) - { - return true; - } - } - } - } - return false; - } - private IDictionary loadingCollections = new Hashtable(); private string loadingRole; --- 3458,3461 ---- *************** *** 3502,3516 **** } - internal LoadingCollectionEntry( PersistentCollection collection, object id, object owner ) - { - _collection = collection; - _id = id; - _owner = owner; - } - public PersistentCollection Collection { get { return _collection; } - set { _collection = value; } } --- 3474,3480 ---- *************** *** 3518,3522 **** { get { return _id; } - set { _id = value; } } --- 3482,3485 ---- *************** *** 3542,3547 **** /// <param name="id"></param> /// <returns></returns> - // TODO: replace with owner version of this method... - [Obsolete( "Use the one with CollectionPersister, id, owner) instead" )] public PersistentCollection GetLoadingCollection( CollectionPersister persister, object id ) { --- 3505,3508 ---- *************** *** 3579,3583 **** lce.Collection.EndRead(); AddInitializedCollection( lce.Collection, persister, lce.Id ); ! persister.Cache( lce.Id, lce.Collection, this ); } } --- 3540,3548 ---- lce.Collection.EndRead(); AddInitializedCollection( lce.Collection, persister, lce.Id ); ! // h2.1 synch - added the IsCacheable to nh specifically ! if( persister.HasCache && lce.Collection.IsCacheable ) ! { ! persister.Cache.Put( lce.Id, lce.Collection.Disassemble( persister ), Timestamp ); ! } } } *************** *** 3809,3817 **** log.Debug( "checking second-level cache" ); ! // TODO: ! //bool foundInCache = InitializeCollectionFromCache(ce.loadedKey, GetCollectionOwner(ce), ! // ce.loadedPersister, collection); ! bool foundInCache = false; ! if (foundInCache) { --- 3774,3779 ---- log.Debug( "checking second-level cache" ); ! bool foundInCache = InitializeCollectionFromCache(ce.loadedKey, GetCollectionOwner(ce), ce.loadedPersister, collection); ! if (foundInCache) { *************** *** 3843,3847 **** // Collections are still written to the Cache in EndLoadingCollection and that // is probably the most appropriate place for that code anyway. ! // if (!writing) persister.Cache(id, collection, this); log.Debug("collection initialized"); } --- 3805,3816 ---- // Collections are still written to the Cache in EndLoadingCollection and that // is probably the most appropriate place for that code anyway. ! if (!writing) ! { ! // h2.1 synch - added the IsCacheable to nh specifically ! if( persister.HasCache && collection.IsCacheable ) ! { ! persister.Cache.Put( id, collection.Disassemble( persister ), Timestamp ); ! } ! } log.Debug("collection initialized"); } *************** *** 4400,4466 **** } ! //remove all collections for the entity ! EvictCollections( persister.GetPropertyValues( obj ), persister.PropertyTypes ); Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionEvict, CascadePoint.CascadeOnEvict, null ); } ! ! /// <summary> ! /// Evict any collections referenced by the object from the session cache. This will NOT ! /// pick up any collections that were dereferenced, so they will be deleted (suboptimal ! /// but not exactly incorrect). ! /// </summary> ! /// <param name="values"></param> ! /// <param name="types"></param> ! private void EvictCollections( Object[ ] values, IType[ ] types ) { ! for( int i = 0; i < types.Length; i++ ) { ! if( values[ i ] == null ) { ! // do nothing } ! else if( types[ i ].IsPersistentCollectionType ) ! { ! object pc = null; ! if( ( ( PersistentCollectionType ) types[ i ] ).IsArrayType ) ! { ! pc = arrayHolders[ values[ i ] ]; ! arrayHolders.Remove( values[ i ] ); ! } ! else if( values[ i ] is PersistentCollection ) ! { ! pc = values[ i ]; ! } ! if( pc != null ) ! { ! PersistentCollection coll = (PersistentCollection) pc; ! if( coll.UnsetSession( this ) ) ! { ! CollectionEntry ce = GetCollectionEntry(coll); ! collectionEntries.Remove(coll); ! if (log.IsDebugEnabled) ! { ! log.Debug( "evicting collection: " ! + MessageHelper.InfoString(ce.loadedPersister, ce.loadedKey) ); ! } ! if ( ce.loadedPersister != null && ce.loadedKey != null ) ! { ! collectionsByKey.Remove( ! new CollectionKey( ce.loadedPersister.Role, ce.loadedKey ) ); ! } ! } ! } } ! else if( types[ i ].IsComponentType ) { ! IAbstractComponentType actype = ( IAbstractComponentType ) types[ i ]; ! EvictCollections( ! actype.GetPropertyValues( values[ i ], this ), ! actype.Subtypes ! ); } } --- 4369,4440 ---- } ! //remove all collections for the entity from the session-level cache ! if( persister.HasCollections ) ! { ! new EvictVisitor(this).Process( obj, persister ); ! } ! Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionEvict, CascadePoint.CascadeOnEvict, null ); } ! internal void EvictCollection(object value, PersistentCollectionType type) { ! object pc; ! if( type.IsArrayType ) { ! pc = arrayHolders[ value ]; ! arrayHolders.Remove(value); ! } ! else ! { ! // the hibernate java coding style is a little different - but ! // doing the same thing ! pc = value as PersistentCollection; ! if( value==null ) { ! return; //EARLY EXIT! } ! } ! PersistentCollection collection = (PersistentCollection)pc; ! if( collection.UnsetSession( this) ) ! { ! EvictCollection( collection ); ! } ! } ! private void EvictCollection(PersistentCollection collection) ! { ! CollectionEntry ce = (CollectionEntry)collectionEntries[collection]; ! collectionEntries.Remove( collection ); ! if( log.IsDebugEnabled ) ! { ! log.Debug( "evicting collection: " + MessageHelper.InfoString( ce.loadedPersister, ce.loadedKey ) ); ! } ! if( ce.loadedPersister!=null && ce.loadedKey!=null ) ! { ! collectionsByKey.Remove( new CollectionKey( ce.loadedPersister.Role, ce.loadedKey ) ); ! } ! } ! ! ! private void EvictCachedCollections(IClassPersister persister, object id) ! { ! EvictCachedCollections( persister.PropertyTypes, id ); ! } ! ! private void EvictCachedCollections(IType[] types, object id) ! { ! foreach( IType type in types ) ! { ! if( type.IsPersistentCollectionType ) ! { ! factory.EvictCollection( ((PersistentCollectionType)type).Role, id ); } ! else if ( type.IsComponentType ) { ! IAbstractComponentType acType = (IAbstractComponentType)type; ! EvictCachedCollections( acType.Subtypes, id ); } } *************** *** 4495,4507 **** if (collection != null) { ! if ( log.IsDebugEnabled ) log.Debug( "returning loading collection:" + MessageHelper.InfoString(persister, id) ); return collection.GetValue(); } else { ! if ( log.IsDebugEnabled ) log.Debug( "creating collection wrapper:" + MessageHelper.InfoString(persister, id) ); collection = persister.CollectionType.Instantiate(this, persister); //TODO: suck into CollectionPersister.instantiate() AddUninitializedCollection(collection, persister, id); --- 4469,4485 ---- if (collection != null) { ! if ( log.IsDebugEnabled ) ! { log.Debug( "returning loading collection:" + MessageHelper.InfoString(persister, id) ); + } return collection.GetValue(); } else { ! if ( log.IsDebugEnabled ) ! { log.Debug( "creating collection wrapper:" + MessageHelper.InfoString(persister, id) ); + } collection = persister.CollectionType.Instantiate(this, persister); //TODO: suck into CollectionPersister.instantiate() AddUninitializedCollection(collection, persister, id); *************** *** 4518,4521 **** --- 4496,4527 ---- } } + + /// <summary> + /// Try to initialize a Collection from the cache. + /// </summary> + /// <param name="id"></param> + /// <param name="owner"></param> + /// <param name="persister"></param> + /// <param name="collection"></param> + /// <returns><c>true</c> if the collection was initialized from the cache, otherwise <c>false</c>.</returns> + private bool InitializeCollectionFromCache(object id, object owner, CollectionPersister persister, PersistentCollection collection) + { + if( persister.HasCache==false ) + { + return false; + } + + object cached = persister.Cache.Get( id, Timestamp ); + if( cached==null ) + { + return false; + } + + collection.InitializeFromCache( persister, cached, owner ); + GetCollectionEntry( collection ).PostInitialize( collection ); + //addInitializedCollection(collection, persister, id); h2.1 - commented out + return true; + + } } } \ No newline at end of file Index: ProxyVisitor.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/ProxyVisitor.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** ProxyVisitor.cs 6 Feb 2005 01:59:02 -0000 1.1 --- ProxyVisitor.cs 7 Feb 2005 01:34:41 -0000 1.2 *************** *** 1,12 **** using System; - using System.Collections; - using NHibernate.Type; namespace NHibernate.Impl { internal abstract class ProxyVisitor : AbstractVisitor { ! public ProxyVisitor(SessionImpl session) : base(session) { } --- 1,13 ---- using System; using NHibernate.Type; namespace NHibernate.Impl { + /// <summary> + /// Reassociates uninitialized Proxies with the Session. + /// </summary> internal abstract class ProxyVisitor : AbstractVisitor { ! public ProxyVisitor(SessionImpl session) : base( session ) { } *************** *** 14,20 **** protected override object ProcessEntity(object value, EntityType entityType) { ! if (value != null) { ! Session.ReassociateIfUninitializedProxy(value); // if it is an initialized proxy, let cascade // handle it later on --- 15,21 ---- protected override object ProcessEntity(object value, EntityType entityType) { ! if( value != null ) { ! Session.ReassociateIfUninitializedProxy( value ); // if it is an initialized proxy, let cascade // handle it later on *************** *** 24,26 **** } } ! } --- 25,27 ---- } } ! } \ No newline at end of file Index: WrapVisitor.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/WrapVisitor.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** WrapVisitor.cs 6 Feb 2005 01:59:02 -0000 1.1 --- WrapVisitor.cs 7 Feb 2005 01:34:41 -0000 1.2 *************** *** 1,32 **** using System; ! using System.Collections; ! using NHibernate.Collection; ! using NHibernate.Engine; using NHibernate.Type; - using log4net; - namespace NHibernate.Impl { internal class WrapVisitor : ProxyVisitor { ! private static readonly ILog log = LogManager.GetLogger(typeof(WrapVisitor)); ! private bool substitute = false; public bool IsSubstitutionRequired { ! get { return substitute; } } ! public WrapVisitor(SessionImpl session) : base (session) { } protected override object ProcessCollection(object collection, PersistentCollectionType collectionType) { ! if ( collection is PersistentCollection ) { ! PersistentCollection coll = (PersistentCollection) collection; ! if ( coll.SetCurrentSession(Session) ) { Session.ReattachCollection( coll, coll.CollectionSnapshot ); --- 1,34 ---- using System; ! using log4net; using NHibernate.Collection; ! using NHibernate.Persister; using NHibernate.Type; namespace NHibernate.Impl { + /// <summary> + /// Wrap collections in a NHibernate collection wrapper. + /// </summary> internal class WrapVisitor : ProxyVisitor { ! private static readonly ILog log = LogManager.GetLogger( typeof( WrapVisitor ) ); ! private bool _substitute = false; public bool IsSubstitutionRequired { ! get { return _substitute; } } ! public WrapVisitor(SessionImpl session) : base( session ) ! { ! } protected override object ProcessCollection(object collection, PersistentCollectionType collectionType) { ! if( collection is PersistentCollection ) { ! PersistentCollection coll = (PersistentCollection)collection; ! if( coll.SetCurrentSession( Session ) ) { Session.ReattachCollection( coll, coll.CollectionSnapshot ); *************** *** 35,41 **** } ! else { ! return ProcessArrayOrNewCollection(collection, collectionType); } } --- 37,43 ---- } ! else { ! return ProcessArrayOrNewCollection( collection, collectionType ); } } *************** *** 43,68 **** private object ProcessArrayOrNewCollection(object collection, PersistentCollectionType collectionType) { ! if (collection == null) return null; CollectionPersister persister = Session.GetCollectionPersister( collectionType.Role ); ! ! if ( collectionType.IsArrayType ) { ! ArrayHolder ah = Session.GetArrayHolder(collection); ! if (ah == null) { ! ah = new ArrayHolder(Session, collection); ! Session.AddNewCollection(ah, persister); ! Session.AddArrayHolder(ah); } return null; } ! else { ! PersistentCollection persistentCollection = collectionType.Wrap(Session, collection); ! Session.AddNewCollection(persistentCollection, persister); ! ! if ( log.IsDebugEnabled ) log.Debug( "Wrapped collection in role: " + collectionType.Role ); ! return persistentCollection; //Force a substitution! } --- 45,76 ---- private object ProcessArrayOrNewCollection(object collection, PersistentCollectionType collectionType) { ! if( collection == null ) ! { ! return null; ! } CollectionPersister persister = Session.GetCollectionPersister( collectionType.Role ); ! ! if( collectionType.IsArrayType ) { ! ArrayHolder ah = Session.GetArrayHolder( collection ); ! if( ah == null ) { ! ah = new ArrayHolder( Session, collection ); ! Session.AddNewCollection( ah, persister ); ! Session.AddArrayHolder( ah ); } return null; } ! else { ! PersistentCollection persistentCollection = collectionType.Wrap( Session, collection ); ! Session.AddNewCollection( persistentCollection, persister ); ! ! if( log.IsDebugEnabled ) ! { ! log.Debug( "Wrapped collection in role: " + collectionType.Role ); ! } ! return persistentCollection; //Force a substitution! } *************** *** 71,81 **** public override void ProcessValues(object[] values, IType[] types) { ! for (int i = 0; i < types.Length; i++) { ! object result = ProcessValue( values[i], types[i] ); ! if ( result != null ) { ! substitute = true; ! values[i] = result; } } --- 79,89 ---- public override void ProcessValues(object[] values, IType[] types) { ! for( int i = 0; i < types.Length; i++ ) { ! object result = ProcessValue( values[ i ], types[ i ] ); ! if( result != null ) { ! _substitute = true; ! values[ i ] = result; } } *************** *** 84,105 **** protected override object ProcessComponent(object component, IAbstractComponentType componentType) { ! if (component == null) return null; object[] values = componentType.GetPropertyValues( component, Session ); IType[] types = componentType.Subtypes; bool substituteComponent = false; ! for ( int i=0; i<types.Length; i++ ) { ! object result = ProcessValue( values[i], types[i] ); ! if (result != null) { substituteComponent = true; ! values[i] = result; } } ! if (substituteComponent) { ! componentType.SetPropertyValues(component, values); } --- 92,116 ---- protected override object ProcessComponent(object component, IAbstractComponentType componentType) { ! if( component == null ) ! { ! return null; ! } object[] values = componentType.GetPropertyValues( component, Session ); IType[] types = componentType.Subtypes; bool substituteComponent = false; ! for( int i = 0; i < types.Length; i++ ) { ! object result = ProcessValue( values[ i ], types[ i ] ); ! if( result != null ) { substituteComponent = true; ! values[ i ] = result; } } ! if( substituteComponent ) { ! componentType.SetPropertyValues( component, values ); } *************** *** 107,118 **** } ! public override void Process(object obj, NHibernate.Persister.IClassPersister persister) { ! object[] values = persister.GetPropertyValues(obj); IType[] types = persister.PropertyTypes; ! ProcessValues(values, types); ! if ( IsSubstitutionRequired ) persister.SetPropertyValues(obj, values); } } ! } --- 118,132 ---- } ! public override void Process(object obj, IClassPersister persister) { ! object[] values = persister.GetPropertyValues( obj ); IType[] types = persister.PropertyTypes; ! ProcessValues( values, types ); ! if( IsSubstitutionRequired ) ! { ! persister.SetPropertyValues( obj, values ); ! } } } ! } \ No newline at end of file Index: SessionFactoryImpl.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/SessionFactoryImpl.cs,v retrieving revision 1.40 retrieving revision 1.41 diff -C2 -d -r1.40 -r1.41 *** SessionFactoryImpl.cs 30 Jan 2005 19:36:15 -0000 1.40 --- SessionFactoryImpl.cs 7 Feb 2005 01:34:41 -0000 1.41 *************** *** 784,788 **** if( p.HasCache ) { ! p.CacheConcurrencyStrategy.Destroy(); } } --- 784,788 ---- if( p.HasCache ) { ! p.Cache.Destroy(); } } *************** *** 835,839 **** if( p.HasCache ) { ! p.CacheConcurrencyStrategy.Remove( id ); } } --- 835,839 ---- if( p.HasCache ) { ! p.Cache.Remove( id ); } } *************** *** 848,852 **** if( p.HasCache ) { ! p.CacheConcurrencyStrategy.Clear(); } } --- 848,852 ---- if( p.HasCache ) { ! p.Cache.Clear(); } } --- NEW FILE: DirtyCollectionSearchVisitor.cs --- using System; using NHibernate.Collection; using NHibernate.Type; namespace NHibernate.Impl { /// <summary> /// A Visitor that determines if a dirty collection was found. /// </summary> /// <remarks> /// <list type="number"> /// <listheader> /// <description>Reason for dirty collection</description> /// </listheader> /// <item> /// <description> /// If it is a new application-instantiated collection, return true (does not occur anymore!) /// </description> /// </item> /// <item> /// <description> /// If it is a component, recurse. /// </description> /// </item> /// <item> /// <description> /// If it is a wrapped collection, ask the collection entry. /// </description> /// </item> /// </list> /// </remarks> internal class DirtyCollectionSearchVisitor : AbstractVisitor { private bool _dirty; public DirtyCollectionSearchVisitor(SessionImpl session) : base( session ) { } /// <summary> /// Gets a <see cref="bool"/> indicating if a dirty collection was found. /// </summary> /// <value><c>true</c> if a dirty collection was found.</value> public bool WasDirtyCollectionFound { get { return _dirty; } } protected override object ProcessCollection(object collection, PersistentCollectionType type) { if( collection != null ) { SessionImpl session = Session; PersistentCollection coll; if( type.IsArrayType ) { coll = session.GetArrayHolder( collection ); // if no array holder we found an unwrappered array (this can't occur, // because we now always call wrap() before getting to here) // return (ah==null) ? true : searchForDirtyCollections(ah, type); } else { // if not wrappered yet, its dirty (this can't occur, because // we now always call wrap() before getting to here) // return ( ! (obj is PersistentCollection) ) ? // true : SearchForDirtyCollections( (PersistentCollection) obj, type ); coll = (PersistentCollection)collection; } if( session.CollectionIsDirty( coll ) ) { _dirty = true; return null; // NOTE: early exit } } return null; } } } --- NEW FILE: EvictVisitor.cs --- using System; using NHibernate.Type; namespace NHibernate.Impl { /// <summary> /// Evict any collections referenced by the object from the ISession cache. /// </summary> /// <remarks> /// This will <b>NOT</b> pick up any collections that were dereferenced, so /// they will be deleted (suboptimal but not exactly incorrect). /// </remarks> internal class EvictVisitor : AbstractVisitor { public EvictVisitor(SessionImpl session) : base( session ) { } protected override object ProcessCollection(object collection, PersistentCollectionType type) { if( collection != null ) { Session.EvictCollection( collection, type ); } return null; } } } Index: ReattachVisitor.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/ReattachVisitor.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** ReattachVisitor.cs 6 Feb 2005 01:59:02 -0000 1.1 --- ReattachVisitor.cs 7 Feb 2005 01:34:41 -0000 1.2 *************** *** 1,5 **** using System; - using System.Collections; - using NHibernate.Type; --- 1,3 ---- *************** *** 11,25 **** internal abstract class ReattachVisitor : ProxyVisitor { ! private readonly object key; protected object Key { ! get { return key; } } public ReattachVisitor(SessionImpl session, object key) ! : base(session) { ! this.key = key; } --- 9,23 ---- internal abstract class ReattachVisitor : ProxyVisitor { ! private readonly object _key; protected object Key { ! get { return _key; } } public ReattachVisitor(SessionImpl session, object key) ! : base( session ) { ! _key = key; } *************** *** 27,37 **** { IType[] types = componentType.Subtypes; ! if (component == null) { ! ProcessValues(new object[types.Length], types); } else { ! base.ProcessComponent(component, componentType); } --- 25,35 ---- { IType[] types = componentType.Subtypes; ! if( component == null ) { ! ProcessValues( new object[types.Length], types ); } else { ! base.ProcessComponent( component, componentType ); } *************** *** 39,41 **** } } ! } --- 37,39 ---- } } ! } \ No newline at end of file Index: AbstractVisitor.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/AbstractVisitor.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** AbstractVisitor.cs 6 Feb 2005 01:59:02 -0000 1.1 --- AbstractVisitor.cs 7 Feb 2005 01:34:40 -0000 1.2 *************** *** 1,7 **** using System; - using System.Collections; - - using NHibernate.Type; using NHibernate.Persister; namespace NHibernate.Impl --- 1,5 ---- using System; using NHibernate.Persister; + using NHibernate.Type; namespace NHibernate.Impl *************** *** 14,22 **** internal abstract class AbstractVisitor { ! private readonly SessionImpl session; protected AbstractVisitor(SessionImpl session) { ! this.session = session; } --- 12,20 ---- internal abstract class AbstractVisitor { ! private readonly SessionImpl _session; protected AbstractVisitor(SessionImpl session) { ! _session = session; } *************** *** 28,34 **** public virtual void ProcessValues(object[] values, IType[] types) { ! for (int i = 0; i < values.Length; i++) { ! ProcessValue(values[i], types[i]); } } --- 26,32 ---- public virtual void ProcessValues(object[] values, IType[] types) { ! for( int i = 0; i < values.Length; i++ ) { ! ProcessValue( values[ i ], types[ i ] ); } } *************** *** 36,43 **** protected virtual object ProcessComponent(object component, IAbstractComponentType componentType) { ! if (component != null) { ! ProcessValues(componentType.GetPropertyValues(component, session), ! componentType.Subtypes); } --- 34,41 ---- protected virtual object ProcessComponent(object component, IAbstractComponentType componentType) { ! if( component != null ) { ! ProcessValues( componentType.GetPropertyValues( component, _session ), ! componentType.Subtypes ); } *************** *** 54,71 **** protected object ProcessValue(object value, IType type) { ! if (type.IsPersistentCollectionType) { // Even process null collections ! return ProcessCollection(value, (PersistentCollectionType) type); } ! else if (type.IsEntityType) { ! return ProcessEntity(value, (EntityType) type); } ! else if (type.IsComponentType) { //TODO: what about a null component with a collection! // we also need to clean up that "null collection" ! return ProcessComponent(value, (IAbstractComponentType) type); } else --- 52,69 ---- protected object ProcessValue(object value, IType type) { ! if( type.IsPersistentCollectionType ) { // Even process null collections ! return ProcessCollection( value, (PersistentCollectionType)type ); } ! else if( type.IsEntityType ) { ! return ProcessEntity( value, (EntityType)type ); } ! else if( type.IsComponentType ) { //TODO: what about a null component with a collection! // we also need to clean up that "null collection" ! return ProcessComponent( value, (IAbstractComponentType)type ); } else *************** *** 83,88 **** { ProcessValues( ! persister.GetPropertyValues(obj), ! persister.PropertyTypes); } --- 81,86 ---- { ProcessValues( ! persister.GetPropertyValues( obj ), ! persister.PropertyTypes ); } *************** *** 112,117 **** protected SessionImpl Session { ! get { return session; } } } ! } --- 110,115 ---- protected SessionImpl Session { ! get { return _session; } } } ! } \ No newline at end of file Index: CollectionKey.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/CollectionKey.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** CollectionKey.cs 6 Feb 2005 01:59:02 -0000 1.1 --- CollectionKey.cs 7 Feb 2005 01:34:40 -0000 1.2 *************** *** 1,4 **** using System; - using System.Collections; using NHibernate.Collection; --- 1,3 ---- *************** *** 33,38 **** { int result = 17; ! result = 37 * result + key.GetHashCode(); ! result = 37 * result + role.GetHashCode(); return result; } --- 32,40 ---- { int result = 17; ! unchecked ! { ! result = 37 * result + key.GetHashCode(); ! result = 37 * result + role.GetHashCode(); ! } return result; } Index: OnUpdateVisitor.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/OnUpdateVisitor.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** OnUpdateVisitor.cs 6 Feb 2005 01:59:02 -0000 1.1 --- OnUpdateVisitor.cs 7 Feb 2005 01:34:41 -0000 1.2 *************** *** 1,5 **** using System; - using System.Collections; - using NHibernate.Collection; using NHibernate.Engine; --- 1,3 ---- *************** *** 8,15 **** namespace NHibernate.Impl { internal class OnUpdateVisitor : ReattachVisitor { public OnUpdateVisitor(SessionImpl session, object key) ! : base (session, key) { } --- 6,36 ---- namespace NHibernate.Impl { + /// <summary> + /// When an entity is passed to <c>Update()</c>, all its collections must be + /// inspected and: + /// <list type="number"> + /// <item> + /// <description> + /// Associate any uninitialized PersistentCollections with this Session. + /// </description> + /// </item> + /// <item> + /// <description> + /// Associate any initialized PersistentCollections with this Session, using the + /// existing snapshot. + /// </description> + /// </item> + /// <item> + /// <description> + /// Execute a collection removal (SQL DELETE) for each null collection property + /// or "new" collection. + /// </description> + /// </item> + /// </list> + /// </summary> internal class OnUpdateVisitor : ReattachVisitor { public OnUpdateVisitor(SessionImpl session, object key) ! : base( session, key ) { } *************** *** 18,54 **** { CollectionPersister persister = Session.GetCollectionPersister( type.Role ); ! ! if ( collection is PersistentCollection ) { ! PersistentCollection wrapper = (PersistentCollection) collection; ! if ( wrapper.SetCurrentSession(Session) ) { //a "detached" collection! ICollectionSnapshot snapshot = wrapper.CollectionSnapshot; ! ! if ( !SessionImpl.IsOwnerUnchanged(snapshot, persister, Key) ) { // if the collection belonged to a different entity, // clean up the existing state of the collection ! Session.RemoveCollection(persister, Key); } ! ! Session.ReattachCollection(wrapper, snapshot); } ! else { // a collection loaded in the current session // can not possibly be the collection belonging // to the entity passed to update() ! Session.RemoveCollection(persister, Key); } } ! else { // null or brand new collection // this will also (inefficiently) handle arrays, which have // no snapshot, so we can't do any better ! Session.RemoveCollection(persister, Key); //processArrayOrNewCollection(collection, type); } --- 39,75 ---- { CollectionPersister persister = Session.GetCollectionPersister( type.Role ); ! ! if( collection is PersistentCollection ) { ! PersistentCollection wrapper = (PersistentCollection)collection; ! if( wrapper.SetCurrentSession( Session ) ) { //a "detached" collection! ICollectionSnapshot snapshot = wrapper.CollectionSnapshot; ! ! if( !SessionImpl.IsOwnerUnchanged( snapshot, persister, Key ) ) { // if the collection belonged to a different entity, // clean up the existing state of the collection ! Session.RemoveCollection( persister, Key ); } ! ! Session.ReattachCollection( wrapper, snapshot ); } ! else { // a collection loaded in the current session // can not possibly be the collection belonging // to the entity passed to update() ! Session.RemoveCollection( persister, Key ); } } ! else { // null or brand new collection // this will also (inefficiently) handle arrays, which have // no snapshot, so we can't do any better ! Session.RemoveCollection( persister, Key ); //processArrayOrNewCollection(collection, type); } *************** *** 57,59 **** } } ! } --- 78,80 ---- } } ! } \ No newline at end of file Index: FlushVisitor.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/FlushVisitor.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** FlushVisitor.cs 6 Feb 2005 01:59:02 -0000 1.1 --- FlushVisitor.cs 7 Feb 2005 01:34:40 -0000 1.2 *************** *** 1,5 **** using System; - using System.Collections; - using NHibernate.Collection; using NHibernate.Type; --- 1,3 ---- *************** *** 9,37 **** internal class FlushVisitor : AbstractVisitor { ! private object owner; ! public FlushVisitor( SessionImpl session, object owner ) : base( session ) { ! this.owner = owner; } protected override object ProcessCollection(object collection, PersistentCollectionType type) { ! if (collection!=null) { PersistentCollection coll; ! if ( type.IsArrayType ) { ! coll = Session.GetArrayHolder(collection); } ! else { ! coll = (PersistentCollection) collection; } ! Session.UpdateReachableCollection(coll, type, owner); } return null; } } ! } --- 7,35 ---- internal class FlushVisitor : AbstractVisitor { ! private object _owner; ! public FlushVisitor(SessionImpl session, object owner) : base( session ) { ! _owner = owner; } protected override object ProcessCollection(object collection, PersistentCollectionType type) { ! if( collection != null ) { PersistentCollection coll; ! if( type.IsArrayType ) { ! coll = Session.GetArrayHolder( collection ); } ! else { ! coll = (PersistentCollection)collection; } ! Session.UpdateReachableCollection( coll, type, _owner ); } return null; } } ! } \ No newline at end of file Index: OnLockVisitor.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/OnLockVisitor.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** OnLockVisitor.cs 6 Feb 2005 01:59:02 -0000 1.1 --- OnLockVisitor.cs 7 Feb 2005 01:34:41 -0000 1.2 *************** *** 1,5 **** using System; - using System.Collections; - using NHibernate.Collection; using NHibernate.Engine; --- 1,3 ---- *************** *** 18,22 **** { public OnLockVisitor(SessionImpl session, object key) ! : base(session, key) { } --- 16,20 ---- { public OnLockVisitor(SessionImpl session, object key) ! : base( session, key ) { } *************** *** 24,68 **** protected override object ProcessCollection(object collection, PersistentCollectionType type) { ! CollectionPersister persister = Session.GetCollectionPersister(type.Role); ! if (collection == null) { // Do nothing } ! else if ( collection is PersistentCollection ) { ! PersistentCollection coll = (PersistentCollection) collection; ! if ( coll.SetCurrentSession(Session) ) { ICollectionSnapshot snapshot = coll.CollectionSnapshot; ! if (SessionImpl.IsOwnerUnchanged( snapshot, persister, this.Key )) { // a "detached" collection that originally belonged to the same entity ! if ( snapshot.Dirty ) { ! throw new HibernateException("reassociated object has dirty collection"); } ! Session.ReattachCollection(coll, snapshot); } ! else { // a "detached" collection that belonged to a different entity ! throw new HibernateException("reassociated object has dirty collection reference"); } } ! else { // a collection loaded in the current session // can not possibly be the collection belonging // to the entity passed to update() ! throw new HibernateException("reassociated object has dirty collection reference"); } } ! else { // brand new collection //TODO: or an array!! we can't lock objects with arrays now?? ! throw new HibernateException("reassociated object has dirty collection reference"); } --- 22,66 ---- protected override object ProcessCollection(object collection, PersistentCollectionType type) { ! CollectionPersister persister = Session.GetCollectionPersister( type.Role ); ! if( collection == null ) { // Do nothing } ! else if( collection is PersistentCollection ) { ! PersistentCollection coll = (PersistentCollection)collection; ! if( coll.SetCurrentSession( Session ) ) { ICollectionSnapshot snapshot = coll.CollectionSnapshot; ! if( SessionImpl.IsOwnerUnchanged( snapshot, persister, this.Key ) ) { // a "detached" collection that originally belonged to the same entity ! if( snapshot.Dirty ) { ! throw new HibernateException( "reassociated object has dirty collection" ); } ! Session.ReattachCollection( coll, snapshot ); } ! else { // a "detached" collection that belonged to a different entity ! throw new HibernateException( "reassociated object has dirty collection reference" ); } } ! else { // a collection loaded in the current session // can not possibly be the collection belonging // to the entity passed to update() ! throw new HibernateException( "reassociated object has dirty collection reference" ); } } ! else { // brand new collection //TODO: or an array!! we can't lock objects with arrays now?? ! throw new HibernateException( "reassociated object has dirty collection reference" ); } *************** *** 71,73 **** } ! } --- 69,71 ---- } ! } \ No newline at end of file |