|
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.
|