Skip to content

Commit 2a3313a

Browse files
mickfoldmickfold
authored andcommitted
For NH-1262: Ported cascade delete orphans functionality for one-to-one mappings from Hibernate to NHibernate.
Mappings can be made in XML or MappingByCode. The following scenarios are supported: 1) Bidirectional primary key based one-to-one. 2) Unidirectional shared primary key based one-to-one 3) Bidirectional foreign key based one-to-one with orphan delete declared on the non-constrained (<one-to-one/>) side. 4) Bidirectional foreign key based one-to-one with orphan delete declared on the constrained (<many-to-one unique="true"/>) side. 5) Unidirectional foreign key based one-to-one with orphan delete declared on the constrained (<many-to-one unique="true"/>) side. 6) Unidirectional foreign key based one-to-one with orphan delete declared on the constrained side (<many-to-one unique="true"/>) where the foreign key is composite (multiple columns).
1 parent b434f83 commit 2a3313a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1995
-40
lines changed

doc/reference/modules/basic_mapping.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,7 @@
14251425
name="PropertyName"
14261426
column="column_name"
14271427
class="ClassName"
1428-
cascade="all|none|save-update|delete"
1428+
cascade="all|none|save-update|delete|delete-orphan|all-delete-orphan"
14291429
fetch="join|select"
14301430
update="true|false"
14311431
insert="true|false"
@@ -1585,7 +1585,7 @@
15851585
<programlisting><![CDATA[<one-to-one
15861586
name="PropertyName"
15871587
class="ClassName"
1588-
cascade="all|none|save-update|delete"
1588+
cascade="all|none|save-update|delete|delete-orphan|all-delete-orphan"
15891589
constrained="true|false"
15901590
fetch="join|select"
15911591
property-ref="PropertyNameFromAssociatedClass"

doc/reference/modules/manipulating_data.xml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,14 +1043,15 @@ finally
10431043
</itemizedlist>
10441044

10451045
<para>
1046-
Mapping an association (many-to-one, or collection) with <literal>cascade="all"</literal>
1046+
Mapping an association (many-to-one, one-to-one or collection) with <literal>cascade="all"</literal>
10471047
marks the association as a <emphasis>parent/child</emphasis> style relationship where
10481048
save/update/deletion of the parent results in save/update/deletion of the child(ren).
10491049
Futhermore, a mere reference to a child from a persistent parent will result in save / update
10501050
of the child. The metaphor is incomplete, however. A child which becomes unreferenced by its
1051-
parent is <emphasis>not</emphasis> automatically deleted, except in the case of a
1052-
<literal>&lt;one-to-many&gt;</literal> association mapped with
1053-
<literal>cascade="all-delete-orphan"</literal>. The precise semantics of cascading operations
1051+
parent is <emphasis>not</emphasis> automatically deleted, except in the cases of
1052+
<literal>&lt;one-to-many&gt;</literal> and <literal>&lt;one-to-one&gt;</literal> associations
1053+
that have been mapped with <literal>cascade="all-delete-orphan"</literal> or
1054+
<literal>cascade="delete-orphan"</literal>. The precise semantics of cascading operations
10541055
are as follows:
10551056
</para>
10561057

@@ -1081,8 +1082,8 @@ finally
10811082
<para>
10821083
If a transient child is dereferenced by a persistent parent, <emphasis>nothing
10831084
special happens</emphasis> (the application should explicitly delete the child if
1084-
necessary) unless <literal>cascade="all-delete-orphan"</literal>, in which case the
1085-
"orphaned" child is deleted.
1085+
necessary) unless <literal>cascade="all-delete-orphan"</literal> or
1086+
<literal>cascade="delete-orphan"</literal>, in which case the "orphaned" child is deleted.
10861087
</para>
10871088
</listitem>
10881089
</itemizedlist>

src/NHibernate.Test/MappingByCode/MappersTests/ManyToOneMapperTest.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,6 @@ public void AssignCascadeStyle()
3838
hbm.cascade.Split(',').Select(w => w.Trim()).Should().Contain("persist").And.Contain("delete");
3939
}
4040

41-
[Test]
42-
public void AutoCleanUnsupportedCascadeStyle()
43-
{
44-
var hbmMapping = new HbmMapping();
45-
var hbm = new HbmManyToOne();
46-
var mapper = new ManyToOneMapper(null, hbm, hbmMapping);
47-
mapper.Cascade(Mapping.ByCode.Cascade.Persist | Mapping.ByCode.Cascade.DeleteOrphans | Mapping.ByCode.Cascade.Remove);
48-
hbm.cascade.Split(',').Select(w => w.Trim()).All(w => w.Satisfy(cascade => !cascade.Contains("orphan")));
49-
}
50-
5141
[Test]
5242
public void CanSetAccessor()
5343
{

src/NHibernate.Test/MappingByCode/MappersTests/OneToOneMapperTest.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,6 @@ public void AssignCascadeStyle()
2929
hbm.cascade.Split(',').Select(w => w.Trim()).Should().Contain("persist").And.Contain("delete");
3030
}
3131

32-
[Test]
33-
public void AutoCleanUnsupportedCascadeStyle()
34-
{
35-
var hbm = new HbmOneToOne();
36-
var mapper = new OneToOneMapper(null, hbm);
37-
mapper.Cascade(Mapping.ByCode.Cascade.Persist | Mapping.ByCode.Cascade.DeleteOrphans | Mapping.ByCode.Cascade.Remove);
38-
hbm.cascade.Split(',').Select(w => w.Trim()).All(w => w.Satisfy(cascade => !cascade.Contains("orphan")));
39-
}
40-
4132
[Test]
4233
public void CanSetAccessor()
4334
{
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using NUnit.Framework;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH1262.Hbm.Fk.Bidirectional
4+
{
5+
[TestFixture]
6+
public class DeleteOneToOneOrphansTest : BugTestCase
7+
{
8+
public override string BugNumber
9+
{
10+
get { return "NH1262.Hbm.Fk.Bidirectional"; }
11+
}
12+
13+
protected override void OnSetUp()
14+
{
15+
base.OnSetUp();
16+
using (var s = OpenSession())
17+
using (var t = s.BeginTransaction())
18+
{
19+
var emp = new Employee();
20+
emp.Info = new EmployeeInfo(emp);
21+
22+
s.Save(emp);
23+
t.Commit();
24+
}
25+
}
26+
27+
protected override void OnTearDown()
28+
{
29+
base.OnTearDown();
30+
31+
using (var session = OpenSession())
32+
using (var tx = session.BeginTransaction())
33+
{
34+
session.Delete("from EmployeeInfo");
35+
session.Delete("from Employee");
36+
tx.Commit();
37+
}
38+
}
39+
40+
[Test]
41+
public void TestOrphanedWhileManaged()
42+
{
43+
long empId = 0;
44+
45+
using (var s = OpenSession())
46+
using (var t = s.BeginTransaction())
47+
{
48+
var empInfoResults = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
49+
Assert.AreEqual(1, empInfoResults.Count);
50+
51+
var empResults = s.CreateQuery("from Employee").List<Employee>();
52+
Assert.AreEqual(1, empResults.Count);
53+
54+
var emp = empResults[0];
55+
Assert.NotNull(emp);
56+
57+
empId = emp.Id;
58+
emp.Info = null;
59+
t.Commit();
60+
}
61+
62+
using (var s = OpenSession())
63+
using (var t = s.BeginTransaction())
64+
{
65+
var emp = s.Get<Employee>(empId);
66+
Assert.Null(emp.Info);
67+
68+
var empInfoResults = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
69+
Assert.AreEqual(0, empInfoResults.Count);
70+
71+
var empResults = s.CreateQuery("from Employee").List<Employee>();
72+
Assert.AreEqual(1, empResults.Count);
73+
74+
t.Commit();
75+
}
76+
}
77+
}
78+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3+
namespace="NHibernate.Test.NHSpecificTest.NH1262.Hbm.Fk.Bidirectional"
4+
assembly="NHibernate.Test" >
5+
6+
<class name="Employee">
7+
<id name="Id" type="long" column="Id" >
8+
<generator class="increment" />
9+
</id>
10+
<one-to-one name="Info"
11+
property-ref="EmployeeDetails"
12+
class="EmployeeInfo"
13+
constrained="false"
14+
cascade="all,delete-orphan" />
15+
</class>
16+
17+
<class name="EmployeeInfo">
18+
<id name="Id" type="long" column="Id" >
19+
<generator class="increment" />
20+
</id>
21+
<many-to-one name="EmployeeDetails"
22+
column="employee_id"
23+
unique="true"
24+
not-null="true" />
25+
</class>
26+
27+
</hibernate-mapping>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace NHibernate.Test.NHSpecificTest.NH1262.Hbm.Fk.Bidirectional
2+
{
3+
public class Employee
4+
{
5+
public virtual long Id { get; set; }
6+
public virtual EmployeeInfo Info { get; set; }
7+
8+
public Employee()
9+
{
10+
11+
}
12+
}
13+
14+
public class EmployeeInfo
15+
{
16+
public virtual long Id { get; set; }
17+
public virtual Employee EmployeeDetails { get; set; }
18+
19+
public EmployeeInfo()
20+
{
21+
22+
}
23+
24+
public EmployeeInfo(Employee emp)
25+
{
26+
EmployeeDetails = emp;
27+
}
28+
}
29+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using NUnit.Framework;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH1262.Hbm.Fk.Composite
4+
{
5+
public class DeleteOneToOneOrphansTest : BugTestCase
6+
{
7+
public override string BugNumber
8+
{
9+
get { return "NH1262.Hbm.Fk.Composite"; }
10+
}
11+
12+
protected override void OnSetUp()
13+
{
14+
base.OnSetUp();
15+
using (var s = OpenSession())
16+
using (var t = s.BeginTransaction())
17+
{
18+
var emp = new Employee();
19+
emp.Info = new EmployeeInfo( 1L, 1L);
20+
21+
s.Save(emp.Info);
22+
s.Save(emp);
23+
t.Commit();
24+
}
25+
}
26+
27+
protected override void OnTearDown()
28+
{
29+
base.OnTearDown();
30+
31+
using (var session = OpenSession())
32+
using (var tx = session.BeginTransaction())
33+
{
34+
session.Delete("from EmployeeInfo");
35+
session.Delete("from Employee");
36+
tx.Commit();
37+
}
38+
}
39+
40+
[Test]
41+
public void TestOrphanedWhileManaged()
42+
{
43+
long empId = 0;
44+
45+
using (var s = OpenSession())
46+
using (var t = s.BeginTransaction())
47+
{
48+
var infoList = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
49+
Assert.AreEqual(1, infoList.Count );
50+
51+
var empList = s.CreateQuery("from Employee").List<Employee>();
52+
Assert.AreEqual(1, empList.Count);
53+
54+
var emp = empList[0];
55+
Assert.NotNull(emp.Info);
56+
57+
empId = emp.Id;
58+
emp.Info = null;
59+
60+
t.Commit();
61+
}
62+
63+
using (var s = OpenSession())
64+
using (var t = s.BeginTransaction())
65+
{
66+
var emp = s.Get<Employee>(empId);
67+
Assert.IsNull(emp.Info);
68+
69+
var empInfoList = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
70+
Assert.AreEqual(0, empInfoList.Count);
71+
72+
var empList = s.CreateQuery("from Employee").List<Employee>();
73+
Assert.AreEqual(1, empList.Count);
74+
}
75+
}
76+
}
77+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3+
namespace="NHibernate.Test.NHSpecificTest.NH1262.Hbm.Fk.Composite"
4+
assembly="NHibernate.Test" >
5+
6+
<class name="Employee">
7+
<id name="Id" type="long" column="Id" >
8+
<generator class="increment" />
9+
</id>
10+
<many-to-one name="Info"
11+
unique="true"
12+
not-found="exception"
13+
cascade="all,delete-orphan" >
14+
<column name="COMP_ID" />
15+
<column name="PERS_ID" />
16+
</many-to-one>
17+
</class>
18+
19+
<class name="EmployeeInfo">
20+
<composite-id class="EmployeeInfo+Identifier" name="Id" >
21+
<key-property name="CompanyId" column="COMP_ID" />
22+
<key-property name="PersonId" column="PERS_ID" />
23+
</composite-id>
24+
</class>
25+
26+
</hibernate-mapping>

0 commit comments

Comments
 (0)