From: <ric...@us...> - 2009-08-10 15:31:30
|
Revision: 4687 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4687&view=rev Author: ricbrown Date: 2009-08-10 15:31:22 +0000 (Mon, 10 Aug 2009) Log Message: ----------- Fix NH-1914 Modified Paths: -------------- branches/2.1.x/nhibernate/src/NHibernate/Engine/ForeignKeys.cs branches/2.1.x/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs branches/2.1.x/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.cs branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.hbm.xml branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedFixture.cs branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/ branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Fixture.cs branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Mappings.hbm.xml branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Model.cs Modified: branches/2.1.x/nhibernate/src/NHibernate/Engine/ForeignKeys.cs =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate/Engine/ForeignKeys.cs 2009-08-08 17:49:24 UTC (rev 4686) +++ branches/2.1.x/nhibernate/src/NHibernate/Engine/ForeignKeys.cs 2009-08-10 15:31:22 UTC (rev 4687) @@ -1,3 +1,5 @@ +using log4net; +using NHibernate.Id; using NHibernate.Persister.Entity; using NHibernate.Proxy; using NHibernate.Type; @@ -7,6 +9,8 @@ /// <summary> Algorithms related to foreign key constraint transparency </summary> public static class ForeignKeys { + private static readonly ILog log = LogManager.GetLogger(typeof(ForeignKeys)); + public class Nullifier { private readonly bool isDelete; @@ -195,6 +199,17 @@ if (assumed.HasValue) return assumed.Value; + if (persister.IdentifierGenerator is Assigned) + { + // When using assigned identifiers we cannot tell if an entity + // is transient or detached without querying the database. + // This could potentially cause Select N+1 in cascaded saves, so warn the user. + log.Warn("Unable to determine if " + entity.ToString() + + " with assigned identifier " + persister.GetIdentifier(entity, session.EntityMode) + + " is transient or detached; querying the database." + + " Use explicit Save() or Update() in session to prevent this."); + } + // hit the database, after checking the session cache for a snapshot System.Object[] snapshot = session.PersistenceContext.GetDatabaseSnapshot(persister.GetIdentifier(entity, session.EntityMode), persister); Modified: branches/2.1.x/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs 2009-08-08 17:49:24 UTC (rev 4686) +++ branches/2.1.x/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs 2009-08-10 15:31:22 UTC (rev 4687) @@ -3630,7 +3630,19 @@ // check the id unsaved-value bool? result2 = entityMetamodel.IdentifierProperty.UnsavedValue.IsUnsaved(id); if (result2.HasValue) - return result2; + { + if (IdentifierGenerator is Assigned) + { + // if using assigned identifier, we can only make assumptions + // if the value is a known unsaved-value + if (result2.Value) + return true; + } + else + { + return result2; + } + } // check to see if it is in the second-level cache if (HasCache) Added: branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.cs =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.cs (rev 0) +++ branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.cs 2009-08-10 15:31:22 UTC (rev 4687) @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.IdTest +{ + public class Parent + { + public string Id { get; set; } + public string Name { get; set; } + public IList<Child> Children { get; set; } + } + + public class Child + { + public string Id { get; set; } + public Parent Parent { get; set; } + } +} Added: branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.hbm.xml =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.hbm.xml (rev 0) +++ branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedClass.hbm.xml 2009-08-10 15:31:22 UTC (rev 4687) @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false"> + + <class name="NHibernate.Test.IdTest.Parent, NHibernate.Test"> + <id name="Id"> + <generator class="assigned" /> + </id> + + <property name="Name" /> + + <bag name="Children" inverse="true" cascade="all"> + <key column="Parent"/> + <one-to-many class="NHibernate.Test.IdTest.Child, NHibernate.Test"/> + </bag> + </class> + + <class name="NHibernate.Test.IdTest.Child, NHibernate.Test"> + <id name="Id"> + <generator class="assigned" /> + </id> + + <many-to-one name="Parent" class="NHibernate.Test.IdTest.Parent, NHibernate.Test" /> + </class> + +</hibernate-mapping> Added: branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedFixture.cs =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedFixture.cs (rev 0) +++ branches/2.1.x/nhibernate/src/NHibernate.Test/IdTest/AssignedFixture.cs 2009-08-10 15:31:22 UTC (rev 4687) @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using log4net; +using log4net.Core; +using NUnit.Framework; + +namespace NHibernate.Test.IdTest +{ + + [TestFixture] + public class AssignedFixture : IdFixtureBase + { + + private string[] GetAssignedIdentifierWarnings(LogSpy ls) + { + List<string> warnings = new List<string>(); + + foreach (string logEntry in ls.GetWholeLog().Split('\n')) + if (logEntry.Contains("Unable to determine if") && logEntry.Contains("is transient or detached")) + warnings.Add(logEntry); + + return warnings.ToArray(); + } + + protected override string TypeName + { + get { return "Assigned"; } + } + + protected override void OnTearDown() + { + base.OnTearDown(); + + using (ISession s = OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.CreateQuery("delete from Child").ExecuteUpdate(); + s.CreateQuery("delete from Parent").ExecuteUpdate(); + t.Commit(); + } + } + + [Test] + public void SaveOrUpdate_Save() + { + using (LogSpy ls = new LogSpy(LogManager.GetLogger("NHibernate"), Level.Warn)) + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + Parent parent = + new Parent() + { + Id = "parent", + Children = new List<Child>(), + }; + + s.SaveOrUpdate(parent); + t.Commit(); + + long actual = s.CreateQuery("select count(p) from Parent p").UniqueResult<long>(); + Assert.That(actual, Is.EqualTo(1)); + + string[] warnings = GetAssignedIdentifierWarnings(ls); + Assert.That(warnings.Length, Is.EqualTo(1)); + Assert.IsTrue(warnings[0].Contains("parent")); + } + } + + [Test] + public void SaveNoWarning() + { + using (LogSpy ls = new LogSpy(LogManager.GetLogger("NHibernate"), Level.Warn)) + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + Parent parent = + new Parent() + { + Id = "parent", + Children = new List<Child>(), + }; + + s.Save(parent); + t.Commit(); + + long actual = s.CreateQuery("select count(p) from Parent p").UniqueResult<long>(); + Assert.That(actual, Is.EqualTo(1)); + + string[] warnings = GetAssignedIdentifierWarnings(ls); + Assert.That(warnings.Length, Is.EqualTo(0)); + } + } + + [Test] + public void SaveOrUpdate_Update() + { + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + s.Save(new Parent() { Id = "parent", Name = "before" }); + t.Commit(); + } + + using (LogSpy ls = new LogSpy(LogManager.GetLogger("NHibernate"), Level.Warn)) + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + Parent parent = + new Parent() + { + Id = "parent", + Name = "after", + }; + + s.SaveOrUpdate(parent); + t.Commit(); + + string[] warnings = GetAssignedIdentifierWarnings(ls); + Assert.That(warnings.Length, Is.EqualTo(1)); + Assert.IsTrue(warnings[0].Contains("parent")); + } + + using (ISession s = OpenSession()) + { + Parent parent = s.CreateQuery("from Parent").UniqueResult<Parent>(); + Assert.That(parent.Name, Is.EqualTo("after")); + } + } + + [Test] + public void UpdateNoWarning() + { + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + s.Save(new Parent() { Id = "parent", Name = "before" }); + t.Commit(); + } + + using (LogSpy ls = new LogSpy(LogManager.GetLogger("NHibernate"), Level.Warn)) + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + Parent parent = + new Parent() + { + Id = "parent", + Name = "after", + }; + + s.Update(parent); + t.Commit(); + + string[] warnings = GetAssignedIdentifierWarnings(ls); + Assert.That(warnings.Length, Is.EqualTo(0)); + } + + using (ISession s = OpenSession()) + { + Parent parent = s.CreateQuery("from Parent").UniqueResult<Parent>(); + Assert.That(parent.Name, Is.EqualTo("after")); + } + } + + [Test] + public void InsertCascade() + { + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + s.Save(new Child() { Id = "detachedChild" }); + t.Commit(); + } + + using (LogSpy ls = new LogSpy(LogManager.GetLogger("NHibernate"), Level.Warn)) + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + Parent parent = + new Parent() + { + Id = "parent", + Children = new List<Child>(), + }; + + parent.Children.Add(new Child() { Id = "detachedChild", Parent = parent }); + parent.Children.Add(new Child() { Id = "transientChild", Parent = parent }); + + s.Save(parent); + t.Commit(); + + long actual = s.CreateQuery("select count(c) from Child c").UniqueResult<long>(); + Assert.That(actual, Is.EqualTo(2)); + + string[] warnings = GetAssignedIdentifierWarnings(ls); + Assert.That(warnings.Length, Is.EqualTo(2)); + Assert.IsTrue(warnings[0].Contains("detachedChild")); + Assert.IsTrue(warnings[1].Contains("transientChild")); + } + } + + [Test] + public void InsertCascadeNoWarning() + { + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + s.Save(new Child() { Id = "persistedChild" }); + t.Commit(); + } + + using (LogSpy ls = new LogSpy(LogManager.GetLogger("NHibernate"), Level.Warn)) + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + + Parent parent = + new Parent() + { + Id = "parent", + Children = new List<Child>(), + }; + + s.Save(parent); + + Child child1 = s.Load<Child>("persistedChild"); + child1.Parent = parent; + parent.Children.Add(child1); + + Child child2 = new Child() { Id = "transientChild", Parent = parent }; + s.Save(child2); + parent.Children.Add(child2); + + t.Commit(); + + long actual = s.CreateQuery("select count(c) from Child c").UniqueResult<long>(); + Assert.That(actual, Is.EqualTo(2)); + + string[] warnings = GetAssignedIdentifierWarnings(ls); + Assert.That(warnings.Length, Is.EqualTo(0)); + } + } + + } + +} Added: branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Fixture.cs =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Fixture.cs (rev 0) +++ branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Fixture.cs 2009-08-10 15:31:22 UTC (rev 4687) @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH1914 +{ + [TestFixture] + public class Fixture : BugTestCase + { + + [Test] + public void CascadeInsertAssigned() + { + IDS _IDS = new IDS(); + _IDS.Identifier = Guid.NewGuid().ToString(); + _IDS.Name = "IDS"; + _IDS.CRSPLUTs = new Dictionary<String, ListOfHLUT>(); + _IDS.CRSPLUTs.Add("a", new ListOfHLUT()); + + + HLUT _HLUT = new HLUT(); + _HLUT.Identifier = 1123; + _HLUT.Name = "HLUT"; + _HLUT.Entries = new List<Entry>(); + _HLUT.Entries.Add(new Entry(1.1, .1)); + _HLUT.Entries.Add(new Entry(2.2, .2)); + + _IDS.CRSPLUTs["a"].Values.Add(_HLUT); + + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + s.Save(_IDS); + t.Commit(); + } + + using (ISession s = OpenSession()) + { + ITransaction t = s.BeginTransaction(); + IDS _IDSRead = s.Load<IDS>(_IDS.Identifier); + + Assert.IsNotNull(_IDSRead); + Assert.IsNotNull(_IDSRead.CRSPLUTs); + Assert.IsNotNull(_IDSRead.CRSPLUTs["a"]); + Assert.IsNotNull(_IDSRead.CRSPLUTs["a"].Values[0]); + Assert.IsNotNull(_IDSRead.CRSPLUTs["a"].Values[0].Entries); + + s.Delete(_IDSRead); + t.Commit(); + } + } + + } +} Added: branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Mappings.hbm.xml =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Mappings.hbm.xml (rev 0) +++ branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Mappings.hbm.xml 2009-08-10 15:31:22 UTC (rev 4687) @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.NHSpecificTest.NH1914" + default-lazy="false"> + + <class name="IDS" table="IDS"> + <id name="Identifier"> + <generator class="assigned" /> + </id> + <property name="Name" not-null="true" /> + <map name="CRSPLUTs" table="CRSPLUTs" cascade="all-delete-orphan"> + <key column="ParentID"/> + <index column="OrganID" type="String"/> + <one-to-many class="ListOfHLUT"/> + </map> + </class> + + <class name="ListOfHLUT" table="ListOfHLUT"> + <id name="ID" column="CollectionID"> + <generator class="native" /> + </id> + <list name="Values" table="CustomValues" cascade="all"> + <key column="ParentID"/> + <index column="Indexer" type="Int32"/> + <one-to-many class="HLUT"/> + </list> + </class> + + <class name="HLUT" table="HLUT"> + + <!--NotWorking with below id generation.--> + <id name="Identifier" unsaved-value="0"> + <generator class="assigned" /> + </id> + + <!--Working with below id generation.--> + <!-- + <id name="Identifier" unsaved-value="any"> + <generator class="native" /> + </id>--> + + <property name="Name" not-null="true" /> + <list name="Entries" table="Entries" cascade="all"> + <key column="ParentID"/> + <index column="Indexer" type="Int32"/> + <composite-element class="Entry"> + <property name="Key1" type="Double" access="field" /> + <property name="Value" type="Double" access="field"/> + </composite-element> + </list> + </class> + +</hibernate-mapping> Added: branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Model.cs =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Model.cs (rev 0) +++ branches/2.1.x/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1914/Model.cs 2009-08-10 15:31:22 UTC (rev 4687) @@ -0,0 +1,269 @@ +using System; +using System.Xml.Serialization; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +using NHibernate; +using NHibernate.Classic; + +namespace NHibernate.Test.NHSpecificTest.NH1914 +{ + public class IDS + { + public String Identifier { get; set; } + + public String Name { get; set; } + + public IDictionary<String,ListOfHLUT> CRSPLUTs { get; set; } + } + + public class ListOfHLUT : CustomList<HLUT> + { + public ListOfHLUT() : base() { } + public ListOfHLUT(IEnumerable<HLUT> theValues) : base(theValues) { } + } + + public class HLUT : LUT + { + public String Name { get; set; } + } + + public class LUT + { + public long Identifier { get; set; } + + public IList<Entry> Entries { get; set; } + } + + public struct Entry + { + public Entry(Double theKey, Double theValue) + { + Key1 = theKey; + Value = theValue; + } + + public Double Key1; + + public Double Value; + } + + public class CustomList<T> : IList<T>, IList, ILifecycle + { + #region Constructors + + public CustomList() + { + myValues = new List<T>(); + } + + public CustomList(IEnumerable<T> theValues) + { + myValues = new List<T>(theValues); + } + #endregion + + #region Member Variables + protected IList<T> myValues; + #endregion + + #region NHibernate Members + [XmlIgnore] + public virtual String Identifier { get; set; } + + [XmlIgnore] + public virtual long ID { get; set; } + + [XmlIgnore] + public virtual IList<T> Values + { + get + { + return myValues; + } + set + { + myValues = value; + } + } + #endregion + + #region ILifecycle Members + + public LifecycleVeto OnDelete(ISession s) + { + return LifecycleVeto.NoVeto; + } + + public void OnLoad(ISession s, object id) + { + + } + + public LifecycleVeto OnSave(ISession s) + { + return LifecycleVeto.NoVeto; + } + + public LifecycleVeto OnUpdate(ISession s) + { + return LifecycleVeto.NoVeto; + } + + #endregion + + #region IList<T> Members + + public int IndexOf(T item) + { + return myValues.IndexOf(item); + } + + public void Insert(int index, T item) + { + myValues.Insert(index, item); + } + + public void RemoveAt(int index) + { + myValues.RemoveAt(index); + } + + public T this[int index] + { + get + { + return myValues[index]; + } + set + { + myValues[index] = value; + } + } + + #endregion + + #region ICollection<T> Members + + public void Add(T item) + { + myValues.Add(item); + } + + public void Clear() + { + myValues.Clear(); + } + + public bool Contains(T item) + { + return myValues.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + myValues.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return myValues.Count; } + } + + public bool IsReadOnly + { + get { return myValues.IsReadOnly; } + } + + public bool Remove(T item) + { + return myValues.Remove(item); + } + + #endregion + + #region IEnumerable<T> Members + + public IEnumerator<T> GetEnumerator() + { + return myValues.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region IList Members + + public int Add(object value) + { + return ((IList)myValues).Add(value); + } + + public bool Contains(object value) + { + return ((IList)myValues).Contains(value); + } + + public int IndexOf(object value) + { + return ((IList)myValues).IndexOf(value); + } + + public void Insert(int index, object value) + { + ((IList)myValues).Insert(index, value); + } + + public bool IsFixedSize + { + get { return ((IList)myValues).IsFixedSize; } + } + + public void Remove(object value) + { + ((IList)myValues).Remove(value); + } + + object IList.this[int index] + { + get + { + return ((IList)myValues)[index]; + } + set + { + ((IList)myValues)[index] = value; + } + } + + #endregion + + #region ICollection Members + + public void CopyTo(Array array, int index) + { + ((IList)myValues).CopyTo(array, index); + } + + public bool IsSynchronized + { + get { return ((IList)myValues).IsSynchronized; } + } + + public object SyncRoot + { + get { return ((IList)myValues).SyncRoot; } + } + + #endregion + } +} Modified: branches/2.1.x/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- branches/2.1.x/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-08-08 17:49:24 UTC (rev 4686) +++ branches/2.1.x/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-08-10 15:31:22 UTC (rev 4687) @@ -343,6 +343,8 @@ <Compile Include="HQL\Ast\WithClauseFixture.cs" /> <Compile Include="HQL\Ast\Zoo.cs" /> <Compile Include="HQL\BaseFunctionFixture.cs" /> + <Compile Include="IdTest\AssignedClass.cs" /> + <Compile Include="IdTest\AssignedFixture.cs" /> <Compile Include="IdTest\TableGeneratorFixture.cs" /> <Compile Include="LazyOneToOne\Employee.cs" /> <Compile Include="LazyOneToOne\Employment.cs" /> @@ -550,6 +552,8 @@ <Compile Include="NHSpecificTest\NH1907\Fixture.cs" /> <Compile Include="NHSpecificTest\NH1908\Fixture.cs" /> <Compile Include="NHSpecificTest\NH1908\Model.cs" /> + <Compile Include="NHSpecificTest\NH1914\Fixture.cs" /> + <Compile Include="NHSpecificTest\NH1914\Model.cs" /> <Compile Include="NHSpecificTest\NH473\Child.cs" /> <Compile Include="NHSpecificTest\NH473\Fixture.cs" /> <Compile Include="NHSpecificTest\NH473\Parent.cs" /> @@ -1960,6 +1964,8 @@ <EmbeddedResource Include="Bytecode\Lightweight\ProductLine.hbm.xml" /> <EmbeddedResource Include="DriverTest\MultiTypeEntity.hbm.xml" /> <Content Include="DynamicEntity\package.html" /> + <EmbeddedResource Include="NHSpecificTest\NH1914\Mappings.hbm.xml" /> + <EmbeddedResource Include="IdTest\AssignedClass.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1904\Mappings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1908\Mappings.hbm.xml" /> <EmbeddedResource Include="FilterTest\WrongFilterDefInClass.hbm.xml" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |