Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/reference/modules/basic_mapping.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1628,7 +1628,7 @@
name="PropertyName"
column="column_name"
class="ClassName"
cascade="all|none|save-update|delete"
cascade="all|none|save-update|delete|delete-orphan|all-delete-orphan"
fetch="join|select"
update="true|false"
insert="true|false"
Expand Down Expand Up @@ -1788,7 +1788,7 @@
<programlisting><![CDATA[<one-to-one
name="PropertyName"
class="ClassName"
cascade="all|none|save-update|delete"
cascade="all|none|save-update|delete|delete-orphan|all-delete-orphan"
constrained="true|false"
fetch="join|select"
property-ref="PropertyNameFromAssociatedClass"
Expand Down
13 changes: 7 additions & 6 deletions doc/reference/modules/manipulating_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1043,14 +1043,15 @@ finally
</itemizedlist>

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

Expand Down Expand Up @@ -1081,8 +1082,8 @@ finally
<para>
If a transient child is dereferenced by a persistent parent, <emphasis>nothing
special happens</emphasis> (the application should explicitly delete the child if
necessary) unless <literal>cascade="all-delete-orphan"</literal>, in which case the
"orphaned" child is deleted.
necessary) unless <literal>cascade="all-delete-orphan"</literal> or
<literal>cascade="delete-orphan"</literal>, in which case the "orphaned" child is deleted.
</para>
</listitem>
</itemizedlist>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using NUnit.Framework;
using System.Collections;

namespace NHibernate.Test.Cascade.OneToOneCascadeDelete.Hbm.Fk.Bidirectional
{
[TestFixture]
public class DeleteOneToOneOrphansTest : TestCase
{
protected override string MappingsAssembly
{
get { return "NHibernate.Test"; }
}

protected override IList Mappings
{
get { return new string[] { "Cascade.OneToOneCascadeDelete.Hbm.Fk.Bidirectional.Mappings.hbm.xml" }; }
}

protected override void OnSetUp()
{
base.OnSetUp();
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var emp = new Employee();
emp.Info = new EmployeeInfo(emp);

s.Save(emp);
t.Commit();
}
}

protected override void OnTearDown()
{
base.OnTearDown();

using (var session = OpenSession())
using (var tx = session.BeginTransaction())
{
session.Delete("from EmployeeInfo");
session.Delete("from Employee");
tx.Commit();
}
}

[Test]
public void TestOrphanedWhileManaged()
{
long empId = 0;

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var empInfoResults = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
Assert.AreEqual(1, empInfoResults.Count);

var empResults = s.CreateQuery("from Employee").List<Employee>();
Assert.AreEqual(1, empResults.Count);

var emp = empResults[0];
Assert.NotNull(emp);

empId = emp.Id;
emp.Info = null;
t.Commit();
}

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var emp = s.Get<Employee>(empId);
Assert.Null(emp.Info);

var empInfoResults = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
Assert.AreEqual(0, empInfoResults.Count);

var empResults = s.CreateQuery("from Employee").List<Employee>();
Assert.AreEqual(1, empResults.Count);

t.Commit();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernate.Test"
namespace="NHibernate.Test.Cascade.OneToOneCascadeDelete.Hbm.Fk.Bidirectional" >

<class name="Employee">
<id name="Id" type="long" column="Id" >
<generator class="increment" />
</id>
<one-to-one name="Info"
property-ref="EmployeeDetails"
class="EmployeeInfo"
constrained="false"
cascade="all,delete-orphan" />
</class>

<class name="EmployeeInfo">
<id name="Id" type="long" column="Id" >
<generator class="increment" />
</id>
<many-to-one name="EmployeeDetails"
column="employee_id"
unique="true"
not-null="true" />
</class>

</hibernate-mapping>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace NHibernate.Test.Cascade.OneToOneCascadeDelete.Hbm.Fk.Bidirectional
{
public class Employee
{
public virtual long Id { get; set; }
public virtual EmployeeInfo Info { get; set; }

public Employee()
{

}
}

public class EmployeeInfo
{
public virtual long Id { get; set; }
public virtual Employee EmployeeDetails { get; set; }

public EmployeeInfo()
{

}

public EmployeeInfo(Employee emp)
{
EmployeeDetails = emp;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using NUnit.Framework;
using System.Collections;

namespace NHibernate.Test.Cascade.OneToOneCascadeDelete.Hbm.Fk.Composite
{
[TestFixture]
public class DeleteOneToOneOrphansTest : TestCase
{
protected override string MappingsAssembly
{
get { return "NHibernate.Test"; }
}

protected override IList Mappings
{
get { return new string[] { "Cascade.OneToOneCascadeDelete.Hbm.Fk.Composite.Mappings.hbm.xml" }; }
}

protected override void OnSetUp()
{
base.OnSetUp();
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var emp = new Employee();
emp.Info = new EmployeeInfo( 1L, 1L);

s.Save(emp.Info);
s.Save(emp);
t.Commit();
}
}

protected override void OnTearDown()
{
base.OnTearDown();

using (var session = OpenSession())
using (var tx = session.BeginTransaction())
{
session.Delete("from EmployeeInfo");
session.Delete("from Employee");
tx.Commit();
}
}

[Test]
public void TestOrphanedWhileManaged()
{
long empId = 0;

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var infoList = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
Assert.AreEqual(1, infoList.Count );

var empList = s.CreateQuery("from Employee").List<Employee>();
Assert.AreEqual(1, empList.Count);

var emp = empList[0];
Assert.NotNull(emp.Info);

empId = emp.Id;
emp.Info = null;

t.Commit();
}

using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var emp = s.Get<Employee>(empId);
Assert.IsNull(emp.Info);

var empInfoList = s.CreateQuery("from EmployeeInfo").List<EmployeeInfo>();
Assert.AreEqual(0, empInfoList.Count);

var empList = s.CreateQuery("from Employee").List<Employee>();
Assert.AreEqual(1, empList.Count);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernate.Test"
namespace="NHibernate.Test.Cascade.OneToOneCascadeDelete.Hbm.Fk.Composite">

<class name="Employee">
<id name="Id" type="long" column="Id" >
<generator class="increment" />
</id>
<many-to-one name="Info"
unique="true"
not-found="exception"
cascade="all,delete-orphan" >
<column name="COMP_ID" />
<column name="PERS_ID" />
</many-to-one>
</class>

<class name="EmployeeInfo">
<composite-id class="EmployeeInfo+Identifier" name="Id" >
<key-property name="CompanyId" column="COMP_ID" />
<key-property name="PersonId" column="PERS_ID" />
</composite-id>
</class>

</hibernate-mapping>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;

namespace NHibernate.Test.Cascade.OneToOneCascadeDelete.Hbm.Fk.Composite
{
public class Employee
{
public virtual long Id { get; set; }
public virtual EmployeeInfo Info { get; set; }

public Employee()
{

}
}

public class EmployeeInfo
{
public class Identifier
{
public virtual long CompanyId { get; set; }
public virtual long PersonId { get; set; }

public Identifier()
{

}

public Identifier(long companyId, long personId)
{
this.CompanyId = companyId;
this.PersonId = personId;
}


public override bool Equals(Object o)
{
if (this == o)
{
return true;
}

var t = this.GetType();
var u = o.GetType();


if (o == null || !t.IsAssignableFrom(u) || !u.IsAssignableFrom(t))
{
return false;
}

var id = o as Identifier;

return CompanyId.Equals(id.CompanyId)
&& PersonId.Equals(id.PersonId);

}

public override int GetHashCode()
{
return (31 * CompanyId.GetHashCode()) + PersonId.GetHashCode();
}
}

public virtual Identifier Id { get; set; }
public virtual Employee Employee { get; set; }

public EmployeeInfo()
{

}

public EmployeeInfo(long companyId, long personId)
{
this.Id = new Identifier(companyId, personId);
}

public EmployeeInfo(Identifier id)
{
this.Id = id;
}
}
}
Loading