From: <fab...@us...> - 2011-05-30 22:38:08
|
Revision: 5894 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5894&view=rev Author: fabiomaulo Date: 2011-05-30 22:38:01 +0000 (Mon, 30 May 2011) Log Message: ----------- Porting to start fix NH-941 Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Engine/Cascade.cs trunk/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs trunk/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH941/Fixture.cs trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Modified: trunk/nhibernate/src/NHibernate/Engine/Cascade.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Cascade.cs 2011-05-30 22:28:04 UTC (rev 5893) +++ trunk/nhibernate/src/NHibernate/Engine/Cascade.cs 2011-05-30 22:38:01 UTC (rev 5894) @@ -123,7 +123,7 @@ if (style.DoCascade(action)) { - CascadeProperty(persister.GetPropertyValue(parent, i, entityMode), types[i], style, anything, false); + CascadeProperty(parent, persister.GetPropertyValue(parent, i, entityMode), types[i], style, anything, false); } else if (action.RequiresNoCascadeChecking) { @@ -136,7 +136,7 @@ } /// <summary> Cascade an action to the child or children</summary> - private void CascadeProperty(object child, IType type, CascadeStyle style, object anything, bool isCascadeDeleteEnabled) + private void CascadeProperty(object parent, object child, IType type, CascadeStyle style, object anything, bool isCascadeDeleteEnabled) { if (child != null) { @@ -145,12 +145,12 @@ IAssociationType associationType = (IAssociationType)type; if (CascadeAssociationNow(associationType)) { - CascadeAssociation(child, type, style, anything, isCascadeDeleteEnabled); + CascadeAssociation(parent, child, type, style, anything, isCascadeDeleteEnabled); } } else if (type.IsComponentType) { - CascadeComponent(child, (IAbstractComponentType)type, anything); + CascadeComponent(parent, child, (IAbstractComponentType)type, anything); } } } @@ -160,7 +160,7 @@ return associationType.ForeignKeyDirection.CascadeNow(point) && (eventSource.EntityMode != EntityMode.Xml || associationType.IsEmbeddedInXML); } - private void CascadeComponent(object child, IAbstractComponentType componentType, object anything) + private void CascadeComponent(object parent, object child, IAbstractComponentType componentType, object anything) { object[] children = componentType.GetPropertyValues(child, eventSource); IType[] types = componentType.Subtypes; @@ -169,25 +169,25 @@ CascadeStyle componentPropertyStyle = componentType.GetCascadeStyle(i); if (componentPropertyStyle.DoCascade(action)) { - CascadeProperty(children[i], types[i], componentPropertyStyle, anything, false); + CascadeProperty(parent, children[i], types[i], componentPropertyStyle, anything, false); } } } - private void CascadeAssociation(object child, IType type, CascadeStyle style, object anything, bool isCascadeDeleteEnabled) + private void CascadeAssociation(object parent, object child, IType type, CascadeStyle style, object anything, bool isCascadeDeleteEnabled) { if (type.IsEntityType || type.IsAnyType) { - CascadeToOne(child, type, style, anything, isCascadeDeleteEnabled); + CascadeToOne(parent, child, type, style, anything, isCascadeDeleteEnabled); } else if (type.IsCollectionType) { - CascadeCollection(child, style, anything, (CollectionType)type); + CascadeCollection(parent, child, style, anything, (CollectionType)type); } } /// <summary> Cascade an action to a collection</summary> - private void CascadeCollection(object child, CascadeStyle style, object anything, CollectionType type) + private void CascadeCollection(object parent, object child, CascadeStyle style, object anything, CollectionType type) { ICollectionPersister persister = eventSource.Factory.GetCollectionPersister(type.Role); IType elemType = persister.ElementType; @@ -198,24 +198,32 @@ //cascade to current collection elements if (elemType.IsEntityType || elemType.IsAnyType || elemType.IsComponentType) - CascadeCollectionElements(child, type, style, elemType, anything, persister.CascadeDeleteEnabled); + CascadeCollectionElements(parent, child, type, style, elemType, anything, persister.CascadeDeleteEnabled); point = oldCascadeTo; } /// <summary> Cascade an action to a to-one association or any type</summary> - private void CascadeToOne(object child, IType type, CascadeStyle style, object anything, bool isCascadeDeleteEnabled) + private void CascadeToOne(object parent, object child, IType type, CascadeStyle style, object anything, bool isCascadeDeleteEnabled) { string entityName = type.IsEntityType ? ((EntityType)type).GetAssociatedEntityName() : null; if (style.ReallyDoCascade(action)) { //not really necessary, but good for consistency... - action.Cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled); + eventSource.PersistenceContext.AddChildParent(child, parent); + try + { + action.Cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled); + } + finally + { + eventSource.PersistenceContext.RemoveChildParent(child); + } } } /// <summary> Cascade to the collection elements</summary> - private void CascadeCollectionElements(object child, CollectionType collectionType, CascadeStyle style, IType elemType, object anything, bool isCascadeDeleteEnabled) + private void CascadeCollectionElements(object parent, object child, CollectionType collectionType, CascadeStyle style, IType elemType, object anything, bool isCascadeDeleteEnabled) { // we can't cascade to non-embedded elements bool embeddedElements = eventSource.EntityMode != EntityMode.Xml @@ -229,7 +237,7 @@ log.Info("cascade " + action + " for collection: " + collectionType.Role); foreach (object o in action.GetCascadableChildrenIterator(eventSource, collectionType, child)) - CascadeProperty(o, elemType, style, anything, isCascadeDeleteEnabled); + CascadeProperty(parent, o, elemType, style, anything, isCascadeDeleteEnabled); log.Info("done cascade " + action + " for collection: " + collectionType.Role); } Modified: trunk/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs 2011-05-30 22:28:04 UTC (rev 5893) +++ trunk/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs 2011-05-30 22:38:01 UTC (rev 5894) @@ -396,5 +396,18 @@ /// <summary>Is in a two-phase load? </summary> bool IsLoadFinished { get; } + + /// <summary> + /// Add child/parent relation to cache for cascading operations + /// </summary> + /// <param name="child">The child.</param> + /// <param name="parent">The parent.</param> + void AddChildParent(object child, object parent); + + /// <summary> + /// Remove child/parent relation from cache + /// </summary> + /// <param name="child">The child.</param> + void RemoveChildParent(object child); } } Modified: trunk/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs 2011-05-30 22:28:04 UTC (rev 5893) +++ trunk/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs 2011-05-30 22:38:01 UTC (rev 5894) @@ -81,6 +81,11 @@ private Dictionary<CollectionKey, IPersistentCollection> unownedCollections; private bool hasNonReadOnlyEntities; + + // Parent entities cache by their child for cascading + // May be empty or not contains all relation + [NonSerialized] + private IDictionary parentsByChild; [NonSerialized] private int cascading; @@ -116,6 +121,7 @@ collectionEntries = IdentityMap.InstantiateSequenced(InitCollectionSize); collectionsByKey = new Dictionary<CollectionKey, IPersistentCollection>(InitCollectionSize); arrayHolders = IdentityMap.Instantiate(InitCollectionSize); + parentsByChild = IdentityMap.Instantiate(InitCollectionSize); nullifiableEntityKeys = new HashedSet<EntityKey>(); InitTransientState(); } @@ -279,6 +285,7 @@ { loadContexts.Cleanup(); } + parentsByChild.Clear(); } /// <summary>False if we know for certain that all the entities are read-only</summary> @@ -458,6 +465,7 @@ nullifiableEntityKeys.Remove(key); BatchFetchQueue.RemoveBatchLoadableEntityKey(key); BatchFetchQueue.RemoveSubselect(key); + parentsByChild.Clear(); return entity; } @@ -1084,21 +1092,165 @@ /// Search the persistence context for an owner for the child object, /// given a collection role /// </summary> - public object GetOwnerId(string entity, string property, object childObject, IDictionary mergeMap) + public object GetOwnerId(string entityName, string propertyName, object childEntity, IDictionary mergeMap) { - throw new NotImplementedException(); - // TODO persistent context (BackrefPropertyAccessor) + string collectionRole = entityName + '.' + propertyName; + IEntityPersister persister = session.Factory.GetEntityPersister(entityName); + ICollectionPersister collectionPersister = session.Factory.GetCollectionPersister(collectionRole); + + object parent = parentsByChild[childEntity]; + if (parent != null) + { + var entityEntry = (EntityEntry) entityEntries[parent]; + //there maybe more than one parent, filter by type + if (persister.IsSubclassEntityName(entityEntry.EntityName) && IsFoundInParent(propertyName, childEntity, persister, collectionPersister, parent)) + { + return GetEntry(parent).Id; + } + parentsByChild.Remove(childEntity); // remove wrong entry + } + + // iterate all the entities currently associated with the persistence context. + foreach (DictionaryEntry entry in entityEntries) + { + var entityEntry = (EntityEntry) entry.Value; + // does this entity entry pertain to the entity persister in which we are interested (owner)? + if (persister.IsSubclassEntityName(entityEntry.EntityName)) + { + object entityEntryInstance = entry.Key; + + //check if the managed object is the parent + bool found = IsFoundInParent(propertyName, childEntity, persister, collectionPersister, entityEntryInstance); + + if (!found && mergeMap != null) + { + //check if the detached object being merged is the parent + object unmergedInstance = mergeMap[entityEntryInstance]; + object unmergedChild = mergeMap[childEntity]; + if (unmergedInstance != null && unmergedChild != null) + { + found = IsFoundInParent(propertyName, unmergedChild, persister, collectionPersister, unmergedInstance); + } + } + + if (found) + { + return entityEntry.Id; + } + } + } + + // if we get here, it is possible that we have a proxy 'in the way' of the merge map resolution... + // NOTE: decided to put this here rather than in the above loop as I was nervous about the performance + // of the loop-in-loop especially considering this is far more likely the 'edge case' + if (mergeMap != null) + { + foreach (DictionaryEntry mergeMapEntry in mergeMap) + { + var proxy = mergeMapEntry.Key as INHibernateProxy; + if (proxy != null) + { + if (persister.IsSubclassEntityName(proxy.HibernateLazyInitializer.EntityName)) + { + bool found = IsFoundInParent(propertyName, childEntity, persister, collectionPersister, mergeMap[proxy]); + if (!found) + { + found = IsFoundInParent(propertyName, mergeMap[childEntity], persister, collectionPersister, mergeMap[proxy]); + } + if (found) + { + return proxy.HibernateLazyInitializer.Identifier; + } + } + } + } + } + + return null; } + private bool IsFoundInParent(string property, object childEntity, IEntityPersister persister, ICollectionPersister collectionPersister, object potentialParent) + { + object collection = persister.GetPropertyValue(potentialParent, property, session.EntityMode); + return collection != null && NHibernateUtil.IsInitialized(collection) && collectionPersister.CollectionType.Contains(collection, childEntity, session); + } + /// <summary> /// Search the persistence context for an index of the child object, given a collection role /// </summary> - public object GetIndexInOwner(string entity, string property, object childObject, IDictionary mergeMap) + public object GetIndexInOwner(string entity, string property, object childEntity, IDictionary mergeMap) { - throw new NotImplementedException(); - // TODO persistent context (IndexPropertyAccessor) + IEntityPersister persister = session.Factory.GetEntityPersister(entity); + ICollectionPersister cp = session.Factory.GetCollectionPersister(entity + '.' + property); + + // try cache lookup first + object parent = parentsByChild[childEntity]; + if (parent != null) + { + var entityEntry = (EntityEntry) entityEntries[parent]; + //there maybe more than one parent, filter by type + if (persister.IsSubclassEntityName(entityEntry.EntityName)) + { + Object index = GetIndexInParent(property, childEntity, persister, cp, parent); + + if (index == null && mergeMap != null) + { + Object unmergedInstance = mergeMap[parent]; + Object unmergedChild = mergeMap[childEntity]; + if (unmergedInstance != null && unmergedChild != null) + { + index = GetIndexInParent(property, unmergedChild, persister, cp, unmergedInstance); + } + } + if (index != null) + { + return index; + } + } + else + { + parentsByChild.Remove(childEntity); // remove wrong entry + } + } + + //Not found in cache, proceed + foreach (DictionaryEntry me in entityEntries) + { + var ee = (EntityEntry) me.Value; + if (persister.IsSubclassEntityName(ee.EntityName)) + { + object instance = me.Key; + object index = GetIndexInParent(property, childEntity, persister, cp, instance); + + if (index == null && mergeMap != null) + { + object unmergedInstance = mergeMap[instance]; + object unmergedChild = mergeMap[childEntity]; + if (unmergedInstance != null && unmergedChild != null) + { + index = GetIndexInParent(property, unmergedChild, persister, cp, unmergedInstance); + } + } + + if (index != null) + { + return index; + } + } + } + return null; } + private object GetIndexInParent(string property, object childEntity, IEntityPersister persister, ICollectionPersister collectionPersister, object potentialParent) + { + object collection = persister.GetPropertyValue(potentialParent, property, session.EntityMode); + if (collection != null && NHibernateUtil.IsInitialized(collection)) + { + return collectionPersister.CollectionType.IndexOf(collection, childEntity); + } + return null; + } + /// <summary> /// Record the fact that the association belonging to the keyed entity is null. /// </summary> @@ -1196,9 +1348,10 @@ object entity = tempObject; object tempObject2 = entityEntries[entity]; entityEntries.Remove(entity); - EntityEntry oldEntry = (EntityEntry) tempObject2; + var oldEntry = (EntityEntry) tempObject2; + parentsByChild.Clear(); - EntityKey newKey = new EntityKey(generatedId, oldEntry.Persister, Session.EntityMode); + var newKey = new EntityKey(generatedId, oldEntry.Persister, Session.EntityMode); AddEntity(newKey, entity); AddEntry(entity, oldEntry.Status, oldEntry.LoadedState, oldEntry.RowId, generatedId, oldEntry.Version, oldEntry.LockMode, oldEntry.ExistsInDatabase, oldEntry.Persister, oldEntry.IsBeingReplicated, @@ -1213,6 +1366,16 @@ } } + public void AddChildParent(object child, object parent) + { + parentsByChild[child] = parent; + } + + public void RemoveChildParent(object child) + { + parentsByChild.Remove(child); + } + #endregion public override string ToString() @@ -1240,6 +1403,7 @@ // collections to this session, as well as the EntityEntry and // CollectionEntry instances; these associations are transient // because serialization is used for different things. + parentsByChild = IdentityMap.Instantiate(InitCollectionSize); // TODO NH: "reconnect" EntityKey with session.factory and create a test for serialization of StatefulPersistenceContext foreach (DictionaryEntry collectionEntry in collectionEntries) Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH941/Fixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH941/Fixture.cs 2011-05-30 22:28:04 UTC (rev 5893) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH941/Fixture.cs 2011-05-30 22:38:01 UTC (rev 5894) @@ -16,6 +16,7 @@ public virtual int Id { get; set; } } + [Ignore("Not fixed yet")] public class Fixture : TestCaseMappingByCode { protected override HbmMapping GetMappings() Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2011-05-30 22:28:04 UTC (rev 5893) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2011-05-30 22:38:01 UTC (rev 5894) @@ -867,6 +867,7 @@ <Compile Include="NHSpecificTest\NH2733\Model.cs" /> <Compile Include="NHSpecificTest\NH2736\Domain.cs" /> <Compile Include="NHSpecificTest\NH2736\Fixture.cs" /> + <Compile Include="NHSpecificTest\NH941\Fixture.cs" /> <Compile Include="NHSpecificTest\Properties\CompositePropertyRefTest.cs" /> <Compile Include="NHSpecificTest\Properties\DynamicEntityTest.cs" /> <Compile Include="NHSpecificTest\Properties\Model.cs" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |