Skip to content

Commit 000b0d6

Browse files
committed
Fixed both dynamic-update cases for NH-3512
1 parent fac75a2 commit 000b0d6

File tree

6 files changed

+198
-2
lines changed

6 files changed

+198
-2
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace NHibernate.Test.NHSpecificTest.NH3512
2+
{
3+
class Person
4+
{
5+
public virtual int Id { get; protected set; }
6+
public virtual byte[] Version { get; set; }
7+
public virtual string Name { get; set; }
8+
public virtual int Age { get; set; }
9+
}
10+
11+
class Employee : Person
12+
{
13+
public virtual int Salary { get; set; }
14+
}
15+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using NHibernate.Cfg;
2+
using NUnit.Framework;
3+
4+
namespace NHibernate.Test.NHSpecificTest.NH3512
5+
{
6+
7+
8+
public class Fixture : BugTestCase
9+
{
10+
private int _id;
11+
12+
protected override void OnSetUp()
13+
{
14+
using (ISession session = OpenSession())
15+
using (ITransaction transaction = session.BeginTransaction())
16+
{
17+
var employee = new Employee {Name = "Bob", Age = 33, Salary = 100};
18+
session.Save(employee);
19+
20+
transaction.Commit();
21+
22+
_id = employee.Id;
23+
}
24+
}
25+
26+
protected override void OnTearDown()
27+
{
28+
using (ISession session = OpenSession())
29+
using (ITransaction transaction = session.BeginTransaction())
30+
{
31+
session.Delete("from System.Object");
32+
33+
transaction.Commit();
34+
}
35+
}
36+
37+
protected void UpdateBaseEntity()
38+
{
39+
using (ISession session = OpenSession())
40+
using (var transaction = session.BeginTransaction())
41+
{
42+
var person = session.Get<Person>(_id);
43+
44+
var before = person.Version;
45+
46+
person.Age++;
47+
48+
transaction.Commit();
49+
50+
CollectionAssert.AreNotEqual(before, person.Version);
51+
}
52+
}
53+
54+
protected void UpdateDerivedEntity()
55+
{
56+
using (ISession session = OpenSession())
57+
using (var transaction = session.BeginTransaction())
58+
{
59+
var employee = session.Get<Employee>(_id);
60+
61+
var before = employee.Version;
62+
63+
employee.Salary += 10;
64+
65+
transaction.Commit();
66+
67+
CollectionAssert.AreNotEqual(before, employee.Version);
68+
}
69+
}
70+
}
71+
72+
[TestFixture]
73+
public class DynamicUpdateOn : Fixture
74+
{
75+
protected override void Configure(Configuration configuration)
76+
{
77+
foreach (var mapping in configuration.ClassMappings)
78+
{
79+
mapping.DynamicUpdate = true;
80+
}
81+
}
82+
83+
[Test]
84+
public void ShouldChangeVersionWhenBasePropertyChanged()
85+
{
86+
UpdateBaseEntity();
87+
}
88+
89+
[Test]
90+
public void ShouldChangeVersionWhenDerivedPropertyChanged()
91+
{
92+
UpdateDerivedEntity();
93+
}
94+
}
95+
96+
[TestFixture]
97+
public class DynamicUpdateOff : Fixture
98+
{
99+
[Test]
100+
public void ShouldChangeVersionWhenBasePropertyChanged()
101+
{
102+
UpdateBaseEntity();
103+
}
104+
105+
[Test]
106+
public void ShouldChangeVersionWhenDerivedPropertyChanged()
107+
{
108+
UpdateDerivedEntity();
109+
}
110+
}
111+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test" namespace="NHibernate.Test.NHSpecificTest.NH3512">
3+
4+
<class name="Person">
5+
<id name="Id" generator="identity" />
6+
<version name="Version" generated="always" unsaved-value="null" type="BinaryBlob">
7+
<column name="Version" not-null="false" sql-type="timestamp"/>
8+
</version>
9+
<property name="Name" />
10+
<property name="Age" />
11+
12+
<joined-subclass name="Employee">
13+
<key column="Id" />
14+
<property name="Salary" />
15+
</joined-subclass>
16+
</class>
17+
18+
</hibernate-mapping>

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,8 @@
719719
<Compile Include="NHSpecificTest\NH1082\SessionInterceptorThatThrowsExceptionAtBeforeTransactionCompletion.cs" />
720720
<Compile Include="NHSpecificTest\NH3571\Fixture.cs" />
721721
<Compile Include="NHSpecificTest\NH3571\Product.cs" />
722+
<Compile Include="NHSpecificTest\NH3512\Entity.cs" />
723+
<Compile Include="NHSpecificTest\NH3512\Fixture.cs" />
722724
<Compile Include="NHSpecificTest\NH3459\Fixture.cs" />
723725
<Compile Include="NHSpecificTest\NH3459\Order.cs" />
724726
<Compile Include="NHSpecificTest\NH2692\Fixture.cs" />
@@ -3080,6 +3082,7 @@
30803082
<EmbeddedResource Include="Unionsubclass\DatabaseKeyword.hbm.xml" />
30813083
<EmbeddedResource Include="NHSpecificTest\NH1082\Mappings.hbm.xml" />
30823084
<EmbeddedResource Include="NHSpecificTest\NH3571\Mappings.hbm.xml" />
3085+
<EmbeddedResource Include="NHSpecificTest\NH3512\Mappings.hbm.xml" />
30833086
<EmbeddedResource Include="NHSpecificTest\NH3487\Mappings.hbm.xml" />
30843087
<EmbeddedResource Include="NHSpecificTest\NH2692\Mappings.hbm.xml">
30853088
<SubType>Designer</SubType>

src/NHibernate/Persister/Entity/AbstractEntityPersister.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ public virtual bool HasSequentialSelect
11061106
/// The return here is an array of boolean values with each index corresponding
11071107
/// to a given table in the scope of this persister.
11081108
/// </remarks>
1109-
private bool[] GetTableUpdateNeeded(int[] dirtyProperties, bool hasDirtyCollection)
1109+
protected virtual bool[] GetTableUpdateNeeded(int[] dirtyProperties, bool hasDirtyCollection)
11101110
{
11111111
if (dirtyProperties == null)
11121112
{
@@ -3585,7 +3585,7 @@ public bool IsSubclassPropertyNullable(int i)
35853585
/// <summary>
35863586
/// Transform the array of property indexes to an array of booleans, true when the property is dirty
35873587
/// </summary>
3588-
protected bool[] GetPropertiesToUpdate(int[] dirtyProperties, bool hasDirtyCollection)
3588+
protected virtual bool[] GetPropertiesToUpdate(int[] dirtyProperties, bool hasDirtyCollection)
35893589
{
35903590
bool[] propsToUpdate = new bool[entityMetamodel.PropertySpan];
35913591
bool[] updateability = PropertyUpdateability; //no need to check laziness, dirty checking handles that

src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,5 +590,54 @@ public override Declarer GetSubclassPropertyDeclarer(string propertyPath)
590590
}
591591
return base.GetSubclassPropertyDeclarer(propertyPath);
592592
}
593+
594+
protected override bool[] GetTableUpdateNeeded(int[] dirtyProperties, bool hasDirtyCollection)
595+
{
596+
bool[] tableUpdateNeeded = base.GetTableUpdateNeeded(dirtyProperties, hasDirtyCollection);
597+
598+
if (IsVersioned && IsVersionPropertyGenerated)
599+
{
600+
// NH-3512: if this is table-per-subclass inheritance and version property is generated,
601+
// then it should be updated even if no other base class properties changed
602+
603+
tableUpdateNeeded[0] = true;
604+
}
605+
606+
return tableUpdateNeeded;
607+
}
608+
609+
protected override bool[] GetPropertiesToUpdate(int[] dirtyProperties, bool hasDirtyCollection)
610+
{
611+
bool[] propsToUpdate = base.GetPropertiesToUpdate(dirtyProperties, hasDirtyCollection);
612+
613+
if (IsVersioned && IsVersionPropertyGenerated)
614+
{
615+
// NH-3512
616+
// find first updatable property in base class to include it in
617+
bool found = false;
618+
619+
for (int i = 0; i < propsToUpdate.Length; ++i)
620+
{
621+
if (i == VersionProperty || !IsPropertyOfTable(i, 0))
622+
{
623+
continue;
624+
}
625+
626+
if (PropertyUpdateability[i])
627+
{
628+
propsToUpdate[i] = true;
629+
found = true;
630+
break;
631+
}
632+
}
633+
634+
if (!found)
635+
{
636+
// TODO: we failed to find suitable property, so version won't be updated and optimistic concurrency check won't work
637+
}
638+
}
639+
640+
return propsToUpdate;
641+
}
593642
}
594643
}

0 commit comments

Comments
 (0)