From: <fab...@us...> - 2008-11-12 02:40:25
|
Revision: 3907 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=3907&view=rev Author: fabiomaulo Date: 2008-11-12 02:40:14 +0000 (Wed, 12 Nov 2008) Log Message: ----------- Tests for session.Merge Modified Paths: -------------- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate.Test/Operations/ trunk/nhibernate/src/NHibernate.Test/Operations/AbstractOperationTestCase.cs trunk/nhibernate/src/NHibernate.Test/Operations/Address.cs trunk/nhibernate/src/NHibernate.Test/Operations/Competition.cs trunk/nhibernate/src/NHibernate.Test/Operations/Competition.hbm.xml trunk/nhibernate/src/NHibernate.Test/Operations/Competitor.cs trunk/nhibernate/src/NHibernate.Test/Operations/Employee.cs trunk/nhibernate/src/NHibernate.Test/Operations/Employer.cs trunk/nhibernate/src/NHibernate.Test/Operations/Employer.hbm.xml trunk/nhibernate/src/NHibernate.Test/Operations/MergeFixture.cs trunk/nhibernate/src/NHibernate.Test/Operations/Node.cs trunk/nhibernate/src/NHibernate.Test/Operations/Node.hbm.xml trunk/nhibernate/src/NHibernate.Test/Operations/NumberedNode.cs trunk/nhibernate/src/NHibernate.Test/Operations/OneToOne.hbm.xml trunk/nhibernate/src/NHibernate.Test/Operations/OptLockEntity.hbm.xml trunk/nhibernate/src/NHibernate.Test/Operations/Person.cs trunk/nhibernate/src/NHibernate.Test/Operations/PersonalDetails.cs trunk/nhibernate/src/NHibernate.Test/Operations/TimestampedEntity.cs trunk/nhibernate/src/NHibernate.Test/Operations/VersionedEntity.cs Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2008-11-10 17:47:28 UTC (rev 3906) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2008-11-12 02:40:14 UTC (rev 3907) @@ -767,6 +767,19 @@ <Compile Include="Ondelete\ParentChildFixture.cs" /> <Compile Include="Ondelete\Person.cs" /> <Compile Include="Ondelete\Salesperson.cs" /> + <Compile Include="Operations\AbstractOperationTestCase.cs" /> + <Compile Include="Operations\Address.cs" /> + <Compile Include="Operations\Competition.cs" /> + <Compile Include="Operations\Competitor.cs" /> + <Compile Include="Operations\Employee.cs" /> + <Compile Include="Operations\Employer.cs" /> + <Compile Include="Operations\MergeFixture.cs" /> + <Compile Include="Operations\Node.cs" /> + <Compile Include="Operations\NumberedNode.cs" /> + <Compile Include="Operations\Person.cs" /> + <Compile Include="Operations\PersonalDetails.cs" /> + <Compile Include="Operations\TimestampedEntity.cs" /> + <Compile Include="Operations\VersionedEntity.cs" /> <Compile Include="ProjectionFixtures\Key.cs" /> <Compile Include="ProjectionFixtures\Fixture.cs" /> <Compile Include="ProjectionFixtures\NodeType.cs" /> @@ -1520,6 +1533,11 @@ <EmbeddedResource Include="Cascade\JobBatch.hbm.xml" /> <EmbeddedResource Include="Deletetransient\Person.hbm.xml" /> <Content Include="DynamicEntity\package.html" /> + <EmbeddedResource Include="Operations\Competition.hbm.xml" /> + <EmbeddedResource Include="Operations\Employer.hbm.xml" /> + <EmbeddedResource Include="Operations\Node.hbm.xml" /> + <EmbeddedResource Include="Operations\OneToOne.hbm.xml" /> + <EmbeddedResource Include="Operations\OptLockEntity.hbm.xml" /> <EmbeddedResource Include="Subclass\EnumDiscriminator\EnumDiscriminator.hbm.xml" /> <EmbeddedResource Include="Generatedkeys\Select\MyEntity.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1478\Mappings.hbm.xml" /> Added: trunk/nhibernate/src/NHibernate.Test/Operations/AbstractOperationTestCase.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/AbstractOperationTestCase.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/AbstractOperationTestCase.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,58 @@ +using System.Collections; +using NHibernate.Cfg; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace NHibernate.Test.Operations +{ + public abstract class AbstractOperationTestCase : TestCase + { + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + protected override IList Mappings + { + get + { + return new[] + { + "Operations.Node.hbm.xml", "Operations.Employer.hbm.xml", "Operations.OptLockEntity.hbm.xml", + "Operations.OneToOne.hbm.xml", "Operations.Competition.hbm.xml" + }; + } + } + + protected override string CacheConcurrencyStrategy + { + get { return null; } + } + + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Environment.GenerateStatistics, "true"); + configuration.SetProperty(Environment.BatchSize, "0"); + } + + protected void ClearCounts() + { + sessions.Statistics.Clear(); + } + + protected void AssertInsertCount(long expected) + { + Assert.That(sessions.Statistics.EntityInsertCount, Is.EqualTo(expected), "unexpected insert count"); + } + + protected void AssertUpdateCount(int expected) + { + Assert.That(sessions.Statistics.EntityUpdateCount, Is.EqualTo(expected), "unexpected update count"); + } + + protected void AssertDeleteCount(int expected) + { + Assert.That(sessions.Statistics.EntityDeleteCount, Is.EqualTo(expected), "unexpected delete count"); + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Address.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Address.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Address.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,21 @@ +namespace NHibernate.Test.Operations +{ + public class Address + { + private Person resident; + public virtual long Id { get; set; } + public virtual string StreetAddress { get; set; } + public virtual string City { get; set; } + public virtual string Country { get; set; } + + public virtual Person Resident + { + get { return resident; } + set + { + resident = value; + resident.Address = this; + } + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Competition.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Competition.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Competition.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace NHibernate.Test.Operations +{ + public class Competition + { + public Competition() + { + Competitors = new List<Competitor>(); + } + public virtual int Id { get; set; } + public virtual IList<Competitor> Competitors { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Competition.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Competition.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Competition.hbm.xml 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.Operations"> + + <class name="Competition"> + <id name="Id"> + <generator class="native"/> + </id> + <list name="Competitors" table="COMPET_ION_OR" cascade="persist,merge,delete"> + <key column="TION_ID"/> + <list-index column="INDEX_COL"/> + <many-to-many class="Competitor" column="TOR_ID" /> + </list> + </class> + + <class name="Competitor"> + <id name="Id"> + <generator class="native"/> + </id> + <property name="Name"/> + </class> + +</hibernate-mapping> + Added: trunk/nhibernate/src/NHibernate.Test/Operations/Competitor.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Competitor.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Competitor.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,8 @@ +namespace NHibernate.Test.Operations +{ + public class Competitor + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Employee.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Employee.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Employee.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace NHibernate.Test.Operations +{ + public class Employee + { + public virtual int Id { get; set; } + public virtual ICollection<Employer> Employers { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Employer.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Employer.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Employer.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace NHibernate.Test.Operations +{ + public class Employer + { + public virtual int Id { get; set; } + public virtual ICollection<Employee> Employees { get; set; } + public virtual int Vers { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Employer.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Employer.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Employer.hbm.xml 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.Operations"> + + <class name="Employer" polymorphism="explicit"> + <id name="Id"> + <generator class="native"/> + </id> + <version column="vers" name="Vers"/> + <bag name="Employees" cascade="persist,merge" table="EMPLOYER_EMPLOYEE"> + <key column="EMPER_ID"/> + <many-to-many class="Employee" column="EMPEE_ID" /> + </bag> + </class> + + <class name="Employee" polymorphism="explicit"> + <id name="Id"> + <generator class="native"/> + </id> + <bag name="Employers" inverse="true" cascade="persist,merge,save-update" table="EMPLOYER_EMPLOYEE"> + <key column="EMPEE_ID"/> + <many-to-many class="Employer" column="EMPER_ID" /> + </bag> + </class> + +</hibernate-mapping> + Added: trunk/nhibernate/src/NHibernate.Test/Operations/MergeFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/MergeFixture.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/MergeFixture.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,705 @@ +using System.Collections.Generic; +using NHibernate.Criterion; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; + +namespace NHibernate.Test.Operations +{ + [TestFixture] + public class MergeFixture : AbstractOperationTestCase + { + protected override void OnTearDown() + { + Cleanup(); + } + + private void Cleanup() + { + using (ISession s = OpenSession()) + { + using (ITransaction tx = s.BeginTransaction()) + { + s.Delete("from NumberedNode where parent is not null"); + s.Delete("from NumberedNode"); + + s.Delete("from Node where parent is not null"); + s.Delete("from Node"); + + s.Delete("from VersionedEntity where parent is not null"); + s.Delete("from VersionedEntity"); + s.Delete("from TimestampedEntity"); + + s.Delete("from Competitor"); + s.Delete("from Competition"); + + s.Delete("from Employer"); + + tx.Commit(); + } + } + } + + [Test] + public void DeleteAndMerge() + { + using (ISession s = OpenSession()) + { + s.BeginTransaction(); + var jboss = new Employer(); + s.Persist(jboss); + s.Transaction.Commit(); + s.Clear(); + + s.BeginTransaction(); + var otherJboss = s.Get<Employer>(jboss.Id); + s.Delete(otherJboss); + s.Transaction.Commit(); + s.Clear(); + jboss.Vers = 1; + s.BeginTransaction(); + s.Merge(jboss); + s.Transaction.Commit(); + } + } + + [Test] + public void MergeBidiForeignKeyOneToOne() + { + Person p; + Address a; + using (ISession s = OpenSession()) + { + using (ITransaction tx = s.BeginTransaction()) + { + p = new Person {Name = "steve"}; + a = new Address {StreetAddress = "123 Main", City = "Austin", Country = "US", Resident = p}; + s.Persist(a); + s.Persist(p); + tx.Commit(); + } + } + + ClearCounts(); + + p.Address.StreetAddress = "321 Main"; + + using (ISession s = OpenSession()) + { + using (s.BeginTransaction()) + { + p = (Person) s.Merge(p); + s.Transaction.Commit(); + } + } + + AssertInsertCount(0); + AssertUpdateCount(0); // no cascade + AssertDeleteCount(0); + + using (ISession s = OpenSession()) + { + using (ITransaction tx = s.BeginTransaction()) + { + s.Delete(a); + s.Delete(p); + tx.Commit(); + } + } + } + + [Test, Ignore("Need some more investigation about id sync.")] + public void MergeBidiPrimayKeyOneToOne() + { + Person p; + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + p = new Person {Name = "steve"}; + new PersonalDetails {SomePersonalDetail = "I have big feet", Person = p}; + s.Persist(p); + tx.Commit(); + } + + ClearCounts(); + + p.Details.SomePersonalDetail = p.Details.SomePersonalDetail + " and big hands too"; + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + p = (Person) s.Merge(p); + tx.Commit(); + } + + AssertInsertCount(0); + AssertUpdateCount(1); + AssertDeleteCount(0); + + using (ISession s = OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + s.Delete(p); + tx.Commit(); + } + } + + [Test] + public void MergeDeepTree() + { + ClearCounts(); + + ISession s = OpenSession(); + ITransaction tx = s.BeginTransaction(); + var root = new Node {Name = "root"}; + var child = new Node {Name = "child"}; + var grandchild = new Node {Name = "grandchild"}; + root.AddChild(child); + child.AddChild(grandchild); + s.Merge(root); + tx.Commit(); + s.Close(); + + AssertInsertCount(3); + AssertUpdateCount(0); + ClearCounts(); + + grandchild.Description = "the grand child"; + var grandchild2 = new Node {Name = "grandchild2"}; + child.AddChild(grandchild2); + + s = OpenSession(); + tx = s.BeginTransaction(); + s.Merge(root); + tx.Commit(); + s.Close(); + + AssertInsertCount(1); + AssertUpdateCount(1); + ClearCounts(); + + var child2 = new Node {Name = "child2"}; + var grandchild3 = new Node {Name = "grandchild3"}; + child2.AddChild(grandchild3); + root.AddChild(child2); + + s = OpenSession(); + tx = s.BeginTransaction(); + s.Merge(root); + tx.Commit(); + s.Close(); + + AssertInsertCount(2); + AssertUpdateCount(0); + ClearCounts(); + + s = OpenSession(); + tx = s.BeginTransaction(); + s.Delete(grandchild); + s.Delete(grandchild2); + s.Delete(grandchild3); + s.Delete(child); + s.Delete(child2); + s.Delete(root); + tx.Commit(); + s.Close(); + } + + [Test] + public void MergeDeepTreeWithGeneratedId() + { + ClearCounts(); + + NumberedNode root; + NumberedNode child; + NumberedNode grandchild; + using (ISession s = OpenSession()) + { + ITransaction tx = s.BeginTransaction(); + root = new NumberedNode("root"); + child = new NumberedNode("child"); + grandchild = new NumberedNode("grandchild"); + root.AddChild(child); + child.AddChild(grandchild); + root = (NumberedNode) s.Merge(root); + tx.Commit(); + } + + AssertInsertCount(3); + AssertUpdateCount(0); + ClearCounts(); + + IEnumerator<NumberedNode> rit = root.Children.GetEnumerator(); + rit.MoveNext(); + child = rit.Current; + IEnumerator<NumberedNode> cit = child.Children.GetEnumerator(); + cit.MoveNext(); + grandchild = cit.Current; + grandchild.Description = "the grand child"; + var grandchild2 = new NumberedNode("grandchild2"); + child.AddChild(grandchild2); + + using (ISession s = OpenSession()) + { + ITransaction tx = s.BeginTransaction(); + root = (NumberedNode) s.Merge(root); + tx.Commit(); + } + + AssertInsertCount(1); + AssertUpdateCount(1); + ClearCounts(); + + sessions.Evict(typeof (NumberedNode)); + + var child2 = new NumberedNode("child2"); + var grandchild3 = new NumberedNode("grandchild3"); + child2.AddChild(grandchild3); + root.AddChild(child2); + + using (ISession s = OpenSession()) + { + ITransaction tx = s.BeginTransaction(); + root = (NumberedNode) s.Merge(root); + tx.Commit(); + } + + AssertInsertCount(2); + AssertUpdateCount(0); + ClearCounts(); + + using (ISession s = OpenSession()) + { + ITransaction tx = s.BeginTransaction(); + s.Delete("from NumberedNode where name like 'grand%'"); + s.Delete("from NumberedNode where name like 'child%'"); + s.Delete("from NumberedNode"); + tx.Commit(); + } + } + + [Test] + public void MergeManaged() + { + ISession s = OpenSession(); + ITransaction tx = s.BeginTransaction(); + var root = new NumberedNode("root"); + s.Persist(root); + tx.Commit(); + + ClearCounts(); + + tx = s.BeginTransaction(); + var child = new NumberedNode("child"); + root.AddChild(child); + Assert.That(s.Merge(root), Is.SameAs(root)); + IEnumerator<NumberedNode> rit = root.Children.GetEnumerator(); + rit.MoveNext(); + NumberedNode mergedChild = rit.Current; + Assert.That(mergedChild, Is.Not.SameAs(child)); + Assert.That(s.Contains(mergedChild)); + Assert.That(! s.Contains(child)); + Assert.That(root.Children.Count, Is.EqualTo(1)); + Assert.That(root.Children.Contains(mergedChild)); + //assertNotSame( mergedChild, s.Merge(child) ); //yucky :( + tx.Commit(); + + AssertInsertCount(1); + AssertUpdateCount(0); + + Assert.That(root.Children.Count, Is.EqualTo(1)); + Assert.That(root.Children.Contains(mergedChild)); + + tx = s.BeginTransaction(); + Assert.That(s.CreateCriteria(typeof (NumberedNode)).SetProjection(Projections.RowCount()).UniqueResult(), + Is.EqualTo(2)); + tx.Rollback(); + s.Close(); + } + + [Test] + public void MergeManyToManyWithCollectionDeference() + { + // setup base data... + ISession s = OpenSession(); + ITransaction tx = s.BeginTransaction(); + var competition = new Competition(); + competition.Competitors.Add(new Competitor {Name = "Name"}); + competition.Competitors.Add(new Competitor()); + competition.Competitors.Add(new Competitor()); + s.Persist(competition); + tx.Commit(); + s.Close(); + + // the competition graph is now detached: + // 1) create a new List reference to represent the competitors + s = OpenSession(); + tx = s.BeginTransaction(); + var newComp = new List<Competitor>(); + Competitor originalCompetitor = competition.Competitors[0]; + originalCompetitor.Name = "Name2"; + newComp.Add(originalCompetitor); + newComp.Add(new Competitor()); + // 2) set that new List reference unto the Competition reference + competition.Competitors = newComp; + // 3) attempt the merge + var competition2 = (Competition) s.Merge(competition); + tx.Commit(); + s.Close(); + + Assert.That(!(competition == competition2)); + Assert.That(!(competition.Competitors == competition2.Competitors)); + Assert.That(competition2.Competitors.Count, Is.EqualTo(2)); + + s = OpenSession(); + tx = s.BeginTransaction(); + competition = s.Get<Competition>(competition.Id); + Assert.That(competition.Competitors.Count, Is.EqualTo(2)); + s.Delete(competition); + tx.Commit(); + s.Close(); + } + + [Test] + public void MergeStaleVersionFails() + { + ISession s = OpenSession(); + s.BeginTransaction(); + var entity = new VersionedEntity {Id = "entity", Name = "entity"}; + s.Persist(entity); + s.Transaction.Commit(); + s.Close(); + + // make the detached 'entity' reference stale... + s = OpenSession(); + s.BeginTransaction(); + var entity2 = s.Get<VersionedEntity>(entity.Id); + entity2.Name = "entity-name"; + s.Transaction.Commit(); + s.Close(); + + // now try to reattch it + s = OpenSession(); + s.BeginTransaction(); + try + { + s.Merge(entity); + s.Transaction.Commit(); + Assert.Fail("was expecting staleness error"); + } + catch (StaleObjectStateException) + { + // expected outcome... + } + finally + { + s.Transaction.Rollback(); + s.Close(); + Cleanup(); + } + } + + [Test] + public void MergeTree() + { + ClearCounts(); + + ISession s = OpenSession(); + ITransaction tx = s.BeginTransaction(); + var root = new Node {Name = "root"}; + var child = new Node {Name = "child"}; + root.AddChild(child); + s.Persist(root); + tx.Commit(); + s.Close(); + + AssertInsertCount(2); + ClearCounts(); + + root.Description = "The root node"; + child.Description = "The child node"; + + var secondChild = new Node {Name = "second child"}; + + root.AddChild(secondChild); + + s = OpenSession(); + tx = s.BeginTransaction(); + s.Merge(root); + tx.Commit(); + s.Close(); + + AssertInsertCount(1); + AssertUpdateCount(2); + } + + [Test] + public void MergeTreeWithGeneratedId() + { + ClearCounts(); + + ISession s = OpenSession(); + ITransaction tx = s.BeginTransaction(); + var root = new NumberedNode("root"); + var child = new NumberedNode("child"); + root.AddChild(child); + s.Persist(root); + tx.Commit(); + s.Close(); + + AssertInsertCount(2); + ClearCounts(); + + root.Description = "The root node"; + child.Description = "The child node"; + + var secondChild = new NumberedNode("second child"); + + root.AddChild(secondChild); + + s = OpenSession(); + tx = s.BeginTransaction(); + s.Merge(root); + tx.Commit(); + s.Close(); + + AssertInsertCount(1); + AssertUpdateCount(2); + } + + [Test] + public void NoExtraUpdatesOnMerge() + { + ISession s = OpenSession(); + s.BeginTransaction(); + var node = new Node {Name = "test"}; + s.Persist(node); + s.Transaction.Commit(); + s.Close(); + + ClearCounts(); + + // node is now detached, but we have made no changes. so attempt to merge it + // into this new session; this should cause no updates... + s = OpenSession(); + s.BeginTransaction(); + node = (Node) s.Merge(node); + s.Transaction.Commit(); + s.Close(); + + AssertUpdateCount(0); + AssertInsertCount(0); + + /////////////////////////////////////////////////////////////////////// + // as a control measure, now update the node while it is detached and + // make sure we get an update as a result... + node.Description = "new description"; + s = OpenSession(); + s.BeginTransaction(); + node = (Node) s.Merge(node); + s.Transaction.Commit(); + s.Close(); + AssertUpdateCount(1); + AssertInsertCount(0); + /////////////////////////////////////////////////////////////////////// + } + + [Test] + public void NoExtraUpdatesOnMergeVersioned() + { + ISession s = OpenSession(); + s.BeginTransaction(); + var entity = new VersionedEntity {Id = "entity", Name = "entity"}; + s.Persist(entity); + s.Transaction.Commit(); + s.Close(); + + ClearCounts(); + + // entity is now detached, but we have made no changes. so attempt to merge it + // into this new session; this should cause no updates... + s = OpenSession(); + s.BeginTransaction(); + var mergedEntity = (VersionedEntity) s.Merge(entity); + s.Transaction.Commit(); + s.Close(); + + AssertUpdateCount(0); + AssertInsertCount(0); + Assert.That(entity.Version, Is.EqualTo(mergedEntity.Version), "unexpected version increment"); + + /////////////////////////////////////////////////////////////////////// + // as a control measure, now update the node while it is detached and + // make sure we get an update as a result... + entity.Name = "new name"; + s = OpenSession(); + s.BeginTransaction(); + entity = (VersionedEntity) s.Merge(entity); + s.Transaction.Commit(); + s.Close(); + AssertUpdateCount(1); + AssertInsertCount(0); + /////////////////////////////////////////////////////////////////////// + } + + [Test] + public void NoExtraUpdatesOnMergeVersionedWithCollection() + { + ISession s = OpenSession(); + s.BeginTransaction(); + var parent = new VersionedEntity {Id = "parent", Name = "parent"}; + var child = new VersionedEntity {Id = "child", Name = "child"}; + parent.Children.Add(child); + child.Parent = parent; + s.Persist(parent); + s.Transaction.Commit(); + s.Close(); + + ClearCounts(); + + // parent is now detached, but we have made no changes. so attempt to merge it + // into this new session; this should cause no updates... + s = OpenSession(); + s.BeginTransaction(); + var mergedParent = (VersionedEntity) s.Merge(parent); + s.Transaction.Commit(); + s.Close(); + + AssertUpdateCount(0); + AssertInsertCount(0); + Assert.That(parent.Version, Is.EqualTo(mergedParent.Version), "unexpected parent version increment"); + IEnumerator<VersionedEntity> it = mergedParent.Children.GetEnumerator(); + it.MoveNext(); + VersionedEntity mergedChild = it.Current; + Assert.That(child.Version, Is.EqualTo(mergedChild.Version), "unexpected child version increment"); + + /////////////////////////////////////////////////////////////////////// + // as a control measure, now update the node while it is detached and + // make sure we get an update as a result... + mergedParent.Name = "new name"; + mergedParent.Children.Add(new VersionedEntity {Id = "child2", Name = "new child"}); + s = OpenSession(); + s.BeginTransaction(); + parent = (VersionedEntity) s.Merge(mergedParent); + s.Transaction.Commit(); + s.Close(); + AssertUpdateCount(1); + AssertInsertCount(1); + /////////////////////////////////////////////////////////////////////// + } + + [Test] + public void NoExtraUpdatesOnMergeWithCollection() + { + ISession s = OpenSession(); + s.BeginTransaction(); + var parent = new Node {Name = "parent"}; + var child = new Node {Name = "child"}; + parent.Children.Add(child); + child.Parent = parent; + s.Persist(parent); + s.Transaction.Commit(); + s.Close(); + + ClearCounts(); + + // parent is now detached, but we have made no changes. so attempt to merge it + // into this new session; this should cause no updates... + s = OpenSession(); + s.BeginTransaction(); + parent = (Node) s.Merge(parent); + s.Transaction.Commit(); + s.Close(); + + AssertUpdateCount(0); + AssertInsertCount(0); + + /////////////////////////////////////////////////////////////////////// + // as a control measure, now update the node while it is detached and + // make sure we get an update as a result... + IEnumerator<Node> it = parent.Children.GetEnumerator(); + it.MoveNext(); + it.Current.Description = "child's new description"; + parent.Children.Add(new Node {Name = "second child"}); + s = OpenSession(); + s.BeginTransaction(); + parent = (Node) s.Merge(parent); + s.Transaction.Commit(); + s.Close(); + AssertUpdateCount(1); + AssertInsertCount(1); + /////////////////////////////////////////////////////////////////////// + } + + [Test] + public void PersistThenMergeInSameTxnWithTimestamp() + { + ISession s = OpenSession(); + ITransaction tx = s.BeginTransaction(); + var entity = new TimestampedEntity {Id = "test", Name = "test"}; + s.Persist(entity); + s.Merge(new TimestampedEntity {Id = "test", Name = "test-2"}); + + try + { + // control operation... + s.SaveOrUpdate(new TimestampedEntity {Id = "test", Name = "test-3"}); + Assert.Fail("saveOrUpdate() should fail here"); + } + catch (NonUniqueObjectException) + { + // expected behavior + } + + tx.Commit(); + s.Close(); + } + + [Test] + public void PersistThenMergeInSameTxnWithVersion() + { + ISession s = OpenSession(); + ITransaction tx = s.BeginTransaction(); + var entity = new VersionedEntity {Id = "test", Name = "test"}; + s.Persist(entity); + s.Merge(new VersionedEntity {Id = "test", Name = "test-2"}); + + try + { + // control operation... + s.SaveOrUpdate(new VersionedEntity {Id = "test", Name = "test-3"}); + Assert.Fail("saveOrUpdate() should fail here"); + } + catch (NonUniqueObjectException) + { + // expected behavior + } + + tx.Commit(); + s.Close(); + } + + [Test] + public void RecursiveMergeTransient() + { + using (ISession s = OpenSession()) + { + using (ITransaction tx = s.BeginTransaction()) + { + var jboss = new Employer(); + var gavin = new Employee(); + jboss.Employees = new List<Employee> {gavin}; + s.Merge(jboss); + s.Flush(); + jboss = s.CreateQuery("from Employer e join fetch e.Employees").UniqueResult<Employer>(); + Assert.That(NHibernateUtil.IsInitialized(jboss.Employees)); + Assert.That(jboss.Employees.Count, Is.EqualTo(1)); + s.Clear(); + IEnumerator<Employee> it = jboss.Employees.GetEnumerator(); + it.MoveNext(); + + s.Merge(it.Current); + tx.Commit(); + } + } + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Node.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Node.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Node.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,43 @@ +using System; +using Iesi.Collections.Generic; + +namespace NHibernate.Test.Operations +{ + public class Node + { + private ISet<Node> cascadingChildren = new HashedSet<Node>(); + private ISet<Node> children = new HashedSet<Node>(); + private DateTime created = DateTime.Now; + + public virtual string Name { get; set; } + + public virtual string Description { get; set; } + + public virtual DateTime Created + { + get { return created; } + set { created = value; } + } + + public virtual Node Parent { get; set; } + + public virtual ISet<Node> Children + { + get { return children; } + set { children = value; } + } + + public virtual ISet<Node> CascadingChildren + { + get { return cascadingChildren; } + set { cascadingChildren = value; } + } + + public virtual Node AddChild(Node child) + { + children.Add(child); + child.Parent = this; + return this; + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/Node.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Node.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Node.hbm.xml 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.Operations"> + + <class name="Node" polymorphism="explicit"> + <id name="Name"> + <generator class="assigned"/> + </id> + <property name="Description"/> + <many-to-one name="Parent"/> + <property name="Created" not-null="true"/> + <set name="Children" inverse="true" cascade="persist,merge,save-update,evict"> + <key column="parent"/> + <one-to-many class="Node"/> + </set> + <set name="CascadingChildren" inverse="false" cascade="persist,merge,save-update,evict,delete"> + <key column="CASC_PARENT"/> + <one-to-many class="Node"/> + </set> + </class> + + <class name="NumberedNode" polymorphism="explicit"> + <id name="Id" unsaved-value="0"> + <generator class="native"/> + </id> + <property name="Name"> + <column name="name" index="iname" not-null="true"/> + </property> + <property name="Description"/> + <property name="Created" not-null="true"/> + <many-to-one name="Parent" class="NumberedNode"/> + <set name="Children" inverse="true" cascade="persist,merge,save-update" access="field.camelcase"> + <key column="parent"/> + <one-to-many class="NumberedNode"/> + </set> + </class> + +</hibernate-mapping> + Added: trunk/nhibernate/src/NHibernate.Test/Operations/NumberedNode.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/NumberedNode.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/NumberedNode.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Iesi.Collections.Generic; + +namespace NHibernate.Test.Operations +{ + public class NumberedNode + { + private readonly ISet<NumberedNode> children = new HashedSet<NumberedNode>(); + + protected NumberedNode() {} + + public NumberedNode(string name) + { + Name = name; + Created = DateTime.Now; + } + + public virtual long Id { get; set; } + public virtual string Name { get; set; } + public virtual NumberedNode Parent { get; set; } + + public virtual ICollection<NumberedNode> Children + { + get { return children; } + } + + public virtual string Description { get; set; } + public virtual DateTime Created { get; set; } + + public virtual NumberedNode AddChild(NumberedNode child) + { + children.Add(child); + child.Parent = this; + return this; + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/OneToOne.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/OneToOne.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/OneToOne.hbm.xml 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- + Mappings demonstrating bidirectional one-to-one mappings for testing + with various operations. + + Person -> Address is modeled as a bidirectional one to one based on FK. + Person -> Details is modeled as a bidirectional one to one based on PK. +--> + +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.Operations"> + + <class name="Person" table="OPS_PERSON"> + <id name="Id" column="ID" type="long"> + <generator class="increment"/> + </id> + <property name="Name" column="NAME" type="string"/> + <one-to-one name="Address" class="Address" property-ref="Resident" /> + <one-to-one name="Details" class="PersonalDetails" cascade="all" /> + </class> + + <class name="Address" table="OPS_ADDRESS"> + <id name="Id" column="ID" type="long"> + <generator class="increment"/> + </id> + <property name="StreetAddress" column="STREET" type="string" /> + <property name="City" column="CITY" type="string" /> + <property name="Country" column="CTRY" type="string" /> + <many-to-one name="Resident" column="RESIDENT" class="Person" /> + </class> + + <class name="PersonalDetails" table="OPS_PERS_DETAIL"> + <id name="Id" column="ID" type="long"> + <generator class="increment"/> + </id> + <property name="SomePersonalDetail" column="SOME_DETAIL" type="string"/> + <one-to-one name="Person" class="Person" constrained="true" /> + </class> + +</hibernate-mapping> \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/OptLockEntity.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/OptLockEntity.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/OptLockEntity.hbm.xml 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.Operations"> + + <class name="VersionedEntity" table="V_ENTITY"> + <id name="Id" column="ID" type="string"> + <generator class="assigned"/> + </id> + <version name="Version" column="VERS" type="long" /> + <property name="Name" column="NAME" type="string" /> + <many-to-one name="Parent" class="VersionedEntity"/> + <set name="Children" inverse="true" cascade="persist,merge,save-update,evict,delete"> + <key column="parent"/> + <one-to-many class="VersionedEntity"/> + </set> + </class> + + <class name="TimestampedEntity" table="T_ENTITY"> + <id name="Id" column="ID" type="string"> + <generator class="assigned"/> + </id> + <timestamp name="Timestamp" column="TS" /> + <property name="Name" column="NAME" type="string" /> + </class> + +</hibernate-mapping> + Added: trunk/nhibernate/src/NHibernate.Test/Operations/Person.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/Person.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/Person.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,10 @@ +namespace NHibernate.Test.Operations +{ + public class Person + { + public virtual long Id { get; set; } + public virtual string Name { get; set; } + public virtual Address Address { get; set; } + public virtual PersonalDetails Details { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/PersonalDetails.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/PersonalDetails.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/PersonalDetails.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,19 @@ +namespace NHibernate.Test.Operations +{ + public class PersonalDetails + { + private Person person; + public virtual long Id { get; set; } + public virtual string SomePersonalDetail { get; set; } + + public virtual Person Person + { + get { return person; } + set + { + person = value; + person.Details = this; + } + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/TimestampedEntity.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/TimestampedEntity.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/TimestampedEntity.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.Operations +{ + public class TimestampedEntity + { + public virtual string Id { get; set; } + public virtual string Name { get; set; } + public virtual DateTime Timestamp { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Operations/VersionedEntity.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Operations/VersionedEntity.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Operations/VersionedEntity.cs 2008-11-12 02:40:14 UTC (rev 3907) @@ -0,0 +1,18 @@ +using Iesi.Collections.Generic; + +namespace NHibernate.Test.Operations +{ + public class VersionedEntity + { + public VersionedEntity() + { + Children = new HashedSet<VersionedEntity>(); + } + + public virtual string Id { get; set; } + public virtual string Name { get; set; } + public virtual long Version { get; set; } + public virtual VersionedEntity Parent { get; set; } + public virtual ISet<VersionedEntity> Children { get; set; } + } +} \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |