NHibernate mapping child collection and embedded instance of child - nhibernate

I'm trying to figure what's the correct way to map the following parent child relationship. I have a parent class which contains child objects. However, the parent also has a pointer to an instance of one of the children (the PrimaryChild)
Class Parent
Public Property Id As Integer?
Public Property PrimaryChild As Child
Public Property Children As IList(Of Child)
End Class
Public Class Child
Public Property Id As Integer?
Public MyParent As Parent
End Class
Usage is something like
Dim ch As New Child
Dim par as New Parent
ch.MyParent = par
par.Children.Add(ch)
par.PrimaryChild = ch
Session.SaveOrUpdate(par)
However, when I do this, the PrimaryChild is shown as being a null or transient value. I have set cascade="all" on the Children collection.
Any ideas what I'm doing wrong?
Update 1
Added Mappings
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Parent" table="Parents">
<id name="Id" type="System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ID" />
</id>
<set access="nosetter.camelcase-underscore" cascade="all-delete-orphan" inverse="true" name="Children" mutable="true">
<key>
<column name="ParentID" />
</key>
<one-to-many class="Child" />
</set>
<many-to-one cascade="save-update" class="Child" name="PrimaryChild">
<column name="PrimaryChildID" not-null="true" />
</many-to-one>
<many-to-one cascade="save-update" class="Child" name="SecondaryChild">
<column name="SecondaryChildID" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Child" table="Child">
<id name="Id" type="System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ID" />
</id>
<many-to-one class="Parent" name="Parent">
<column name="ParentID" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>

Your tables are looking like this:
Table Parent
(
...
PrimaryChild_FK NOT NULL
)
Table Child
(
...
Paren_FK NOT NULL
)
Can you tell me in which order the data should be inserted? You can neither insert Parent nor Child, since both need the other to set the foreign key. (NHibernate inserts one of them and set the FK to null, to update it later. But the database complains.)
Remove the not null constraint from the set. NHibernate is not smart enough to find a working insert order if you just remove one of them. (AFAIK, not null constraints in the mapping files are actually only used to create the database schema from).
And as already mentioned by mathieu, make the set inverse and use the same foreign key for the child-parent and the parent-children relations.

I've performed this task with tools like the NHibernate LINQ library.
public class Parent {
public Parent() {
Children = new List<Child>();
}
public virtual IList<Child> Children { get; set; }
public virtual Child PrimaryChild {
get {
return Children.FirstOrDefault(x => x.IsPrimary);
}
}
}
If you've already loaded the children, then PrimaryChild is an in-memory operation. If you request PrimaryChild first, then this will be it own database fetch operation. Works nicely, IMO.
And you should look at Andrew Bullock's response. Exposing an IList opens the door to bad domain design. You should only expose the enumeration.

What mappings do you have so far?
PrimaryChild is a one-to-one, Children is a one-to-many but you could manage the relationship in many different ways. Where are your Foreign Keys?
if you put the FK on the Child, then you want both one-to-one and one-to-many mappings with inverse=true set on both.
As an aside, this:
ch.MyParent = par
par.Children.Add(ch)
is a massive OO encapsulation fail. Parent shouldn't expose an IList, as other objects can manipulate it. The parent class should control all manipulations of itself. Make it an IEnumerable and use an AddChild method that does the above two lines.

If your relation is bidirectional (IE parent references children, and child references parent), and if the foreign key is on Child, you need to set the attribute
inverse="true"
in the declaration of the collection. Otherwise cascade won't work nicely :
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Parent">
<id name="Id" />
<bag name="Children" cascade="all" inverse="true">
<key column="ID_PARENT" />
<one-to-many class="Child"/>
</bag>
</class>
<class name="Children">
<id name="Id" />
<many-to-one name="Parent" column="ID_PARENT" class="Parent" not-null="true" />
</class>
</hibernate-mapping>

Related

How can I map to a joined subclass with a different column than the id of parent?

I am working with a brownfield database and am trying to configure a subclass map which joins to its subclasses with a column other than that of the specified id. The login table has a primary key column login_sk which I'd like to use as its id. It joins to two tables via a login_cust_id column (to make things more fun the corresponding columns in the adjoining tables are named differently). If I setup login_cust_id as the id of the UserMap it joins to its subclasses as expected. For what I hope are obvious reasons I do not want to use login_cust_id as the id for my User objects.
public class UserMap : ClassMap<IUser>
{
public UserMap()
{
Table("login");
Id(x => x.Id).Column("login_sk"); // want to setup map like this
// if used instead this works for subclass joining / mapping
// Id(x => x.Id).Column("login_cust_id");
// would prefer to only reference login_cust_id for subclass mapping
}
}
public class CustomerUserMap : SubclassMap<CustomerUser>
{
public CustomerUserMap()
{
Table("customer");
Map(c => c.DisplayName, "cust_mail_name");
Map(c => c.RecordChangeName, "cust_lookup_name");
KeyColumn("cust_id");
}
}
public class EntityUserMap : SubclassMap<EntityUser>
{
public EntityUserMap()
{
Table("entity");
Map(c => c.DisplayName, "entity_name");
KeyColumn("entity_id");
}
}
What I'd like to do is only use the login_cust_id column when joining to subclasses. Is there a fluent mapping setting that allows me to specify this? If not a fluent mapping is there a regular NHibernate XML mapping that would work? I'd prefer to not even map the column and only use it for joining if possible. If it helps there is a potential discriminator column login_holder_type which indicates which table to join to.
It did occur to me to setup an IClassConvention but after poking at the passed IClassInstance I could not determine any settings which would help me.
public class UserIdConvention : IClassConvention, IClassConventionAcceptance
{
public void Apply(IClassInstance instance)
{
// do something awesome with instance.Subclasses to
// specify the use of login_cust_id for subclass joining...
}
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => typeof(User).Equals(x.EntityType));
}
}
The lack of a populated Subclasses collection for the passed instance caused me to look for a more specific inspector which IParentInspector appears to be. Unfortunately Fluent NHibernate does not appear to have corresponding implementations for IParentInstance, IParentConvention or IParentConventionAcceptance like it does for IJoinedSubclassInspector. While I could probably implement my own before I do I wanted to ensure I wasn't barking up the wrong tree.
Is this sort of subclass id adjustment even possible? Am I missing something obvious in either my map or the Fluent NHibernate Conventions namespace? How can I map to a joined subclass with a different column/property than the id of parent?
I was able to think of three possible solution to your problem please see my findings below.
Solution 1: Discriminator based mapping with Join
My initial idea was to use a discriminator based mapping for modelling the inheritance, with each sub-class containing a join with a property ref, i.e
<class name="IUser" abstract="true" table="login">
<id name="Id" column="login_sk">
<generator class="identity"/>
</id>
<discriminator column="login_holder_type" not-null="true" type="System.String"/>
<subclass name="CustomerUser" discriminator-value="Customer">
<join table="customer" >
<key column="cust_id" property-ref="login_cust_id" />
<property name="DisplayName" column="cust_mail_name"/>
<property name="RecordChangeName" column="cust_lookup_name" />
</join>
</subclass>
<subclass name="EntityUser" discriminator-value="Entity">
<join table="entity" >
<key column="entity_id" property-ref="login_cust_id" />
<property name="CompanyName"/>
</join>
</subclass>
</class>
Unfortunately at this time this feature is supported in Hibernate but not in NHibernate. Please see here and here for the outstanding tickets. Some work has gone towards adding this feature which can be seen on this fork on github.
Solution 2: Discriminator based mapping with Many-to-One
Another option is to still use the discriminator based mapping, but use a many-to-one mapping within each of the sub-classes, which would allow you to join on the foreign key using a property-ref. This has the disadvantage of requiring separate classes for all of the properties in your customer and entity tables but is a workable solution.
<class name="IUser" abstract="true" table="login">
<id name="Id" column="login_sk">
<generator class="identity"/>
</id>
<discriminator column="login_holder_type" not-null="true" type="System.String"/>
<subclass name="CustomerUser" discriminator-value="Customer">
<many-to-one name="CustomerProps" property-ref="login_cust_id" />
</subclass>
<subclass name="EntityUser" discriminator-value="entity">
<many-to-one name="EntityProps" property-ref="login_cust_id" />
</subclass>
</class>
<class name="CustomerProps" Table="customer" >
<id name="Id" column="cust_id">
<generator class="assigned"/>
</id>
<property name="DisplayName" column="cust_mail_name"/>
<property name="RecordChangeName" column="cust_lookup_name" />
</class>
<class name="EntityProps" Table="entity" >
<id name="Id" column="entity_id">
<generator class="assigned"/>
</id>
<property name="CompanyName"/>
</class>
Solution 3: Discriminator based mapping with Joins to Updatable Views
The final option is to create an Updatable View in the DB for the customer and entity tables which contains the login_sk field. You can then use Join within each sub-class as you wouldn't require the property-ref.
<class name="IUser" abstract="true" table="login">
<id name="Id" column="login_sk">
<generator class="identity"/>
</id>
<discriminator column="login_holder_type" not-null="true" type="System.String"/>
<subclass name="CustomerUser" discriminator-value="Customer">
<join table="customerView" >
<key column="login_sk" />
<property name="DisplayName" column="cust_mail_name"/>
<property name="RecordChangeName" column="cust_lookup_name" />
</join>
</subclass>
<subclass name="EntityUser" discriminator-value="Entity">
<join table="entityView" >
<key column="login_sk" />
<property name="CompanyName"/>
</join>
</subclass>
</class>

How to reference ID property in relationship with NHibernate?

How do I map relationship, where child endpoint is exposed via Id property and not via whole Parent object?
Here is the example:
class Parent {
public Guid Id { get; set; }
public List<Child> Chlidren { get; set; }
}
class Child {
public Guid Id { get; set; }
public Guid ParentId { get; set; }
}
Here are the equivalent mappings I'm using:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Blabla"
namespace="Blabla"
auto-import="false">
<typedef name="ChildrenList" class="Blabla" />
<class name="Parent" table="Parent" lazy="false">
<id name="Id" column="ID" type="Guid">
<generator class="guid" />
</id>
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false">
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
</class>
<class name="Child" table="Child" lazy="false">
<id name="Id" column="ID" type="Guid">
<generator class="guid" />
</id>
<!-- How to map ParentID here? -->
</class>
</hibernate-mapping>
When I create a parent, add some children to Children collection and then save the parent, everything is fine. But if save a parent object first, then create a child, setting its ParentID property to ID of the parent, then I get
NHibernate.PropertyValueException:
not-null property references a null or transient value Child._Parent.ChildrenBackref
All attempts to map many-to-one relationship resulted in different exceptions while creating NHibernate configuration. Mostly about object type mismatch.
I'm sure NHibernate is capable to handle this scenario. There must something fairly basic that I miss.
EDIT:
I think it make sense to the example test, which fails with above exception:
var child = new Child(Create.Saved<Parent>().Id); // this sets the ParentId property
this.Repository.Save(child); // here I get the exception
My thoughts why NHibernate is raising this: Children property of Parent class mapped in a way that says that a child cannot exist without a parent (<key column="ParentID" not-null="true" />). When I try to persist a child, NHibernate tries to resolve this relationship (to find a parent this child relates to) and fails, since being given no child endpoint (which otherwise would be ParentId property) in the mapping, it check for its own Child._Parent.ChildrenBackref endpoint, whatever it is.
This looks like a desired solution: Mapping ParentId property as child endpoint of the relationship. This would force NHibernate to resolve a parent by using value of ParentId property as parent's primary key.
The thing is I don't know if it's possible.
The one-to-many / many-to-one relationships you have in NHibernate always needs to have a dominant side (i.e. the side that manages the "saving").
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false">
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
The above is a one-to-many relationship where the dominant side is the parent. That means, you save the parent ... and that will save the parent first, then, the children (with the ParentId being null), then a subsequent update will be issued to set the child.ParentId.
Note:
The child is inserted first with ParentId=null ... if you have a db or mapping restriction to say ParentId cannot be null, this action will fail.
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false"
inverse=true>
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
Note the inverse=true attribute. This means the child object is dominant in the relationship, meaning the child object is in charge. The parent will be inserted, then the Id will be assiged to the child.ParentId, and then the child will be inserted with the ParentId already set.
In many cases, of course, you want to go either way. The easiest way to do this is to manage the relationship on both ends (unfortunately, you have to do this yourself).
On the Parent, you have a method:
public void AddChild(Child child)
{
Children.Add(child);
child.ParentId = Id;
}
public void RemoveChild(Child child)
{
Children.Remove(child);
child.ParentId = null;
}
On the Child, you have a method:
public void SetParent(Parent parent)
{
ParentId = parent.Id;
parent.Children.Add(this);
}
Using these methods to Add/Remove/Set, both sides are consistent after the action is performed. It, then, wouldn't matter whether you set inverse=true on the bag or not.
see http://www.nhforge.org/doc/nh/en/index.html#collections-example

NHibernate : many-to-many collection

A customer can have several contact. The code below is working but... the contacts for this customer are deleted first and after inserted again. Is not possible to avoid this delete and just insert the new one ?
<class name="Customer" table="Customer">
<id name="Id">
<generator class="native" />
</id>
<property name="LastName" not-null="true" />
<bag name="Contacts" cascade="all-delete-orphan" table="CustomerContact">
<key column="Customer" not-null="true" />
<many-to-many class="Contact"/>
</bag>l
</class>
<class name="Contact" table="Contact">
<id name="Id">
<generator class="native" />
</id>
<property name="LastName" not-null="true" />
<many-to-one name="Customer" column="Customer" not-null="true" />
</class>
public virtual void AddContact(Contact contact)
{
Contacts.Add(contact);
contact.Customer = this;
}
When I do this code twice, to add 2 contacts :
Contact contact = new Contact() { LastName = "MyLastName" };
Customer customer = session.Get(customerId);
customer.AddContact(contact);
session.Update(customer);
You are using a bag for your collection, a bag can contain duplicates, does not maintain order and there is no way to identify an individual entry in the bag distinctly with SQL.
That is why NH removes and inserts all assigned entities. Use a set if it suits your requirements instead.
This typically happens when NH lost the persistence information about the collection. It assumes that you changed the whole collection. To update the database efficiently, it removes all items in one query (delete ... where customer = 5) and inserts the new items.
You probably don't return the collection provided from NH.
Typical mistake:
IList<Contact> Contacts
{
get { return contacts; }
// wrong: creates a new List and replaces the NH persistent collection
set { contacts = value.ToList(); }
}
By the way, you should make the collection inverse, since it is a redundancy to the contact.Customer relation:
<bag name="Contacts" cascade="all-delete-orphan" table="CustomerContact" inverse="true">
<key column="Customer" not-null="true" />
<many-to-many class="Contact"/>
</bag>

What is the correct way to define many-to-many relationships in NHibernate to allow deletes but avoiding duplicate records

I've been fighting with an NHibernate set-up for a few days now and just can't figure out the correct way to set out my mapping so it works like I'd expect it to.
There's a bit of code to go through before I get to the problems, so apologies in advance for the extra reading.
The setup is pretty simple at the moment, with just these tables:
Category
CategoryId
Name
Item
ItemId
Name
ItemCategory
ItemId
CategoryId
An item can be in many categories and each category can have many items (simple many-to-many relationship).
I have my mapping set out as:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="..."
namespace="...">
<class name="Category" lazy="true">
<id name="CategoryId" unsaved-value="0">
<generator class="native" />
</id>
<property name="Name" />
<bag name="Items" table="ItemCategory" cascade="save-update" inverse="true" generic="true">
<key column="CategoryId"></key>
<many-to-many class="Item" column="ItemId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="..."
namespace="...">
<class name="Item" table="Item" lazy="true">
<id name="ItemId" unsaved-value="0">
<generator class="native" />
</id>
<property name="Name" />
<bag name="Categories" table="ItemCategory" cascade="save-update" generic="true">
<key column="ItemId"></key>
<many-to-many class="Category" column="CategoryId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
My methods for adding items to the Item list in Category and Category list in Item set both sides of the relationship.
In Item:
public virtual IList<Category> Categories { get; protected set; }
public virtual void AddToCategory(Category category)
{
if (Categories == null)
Categories = new List<Category>();
if (!Categories.Contains(category))
{
Categories.Add(category);
category.AddItem(this);
}
}
In Category:
public virtual IList<Item> Items { get; protected set; }
public virtual void AddItem(Item item)
{
if (Items == null)
Items = new List<Item>();
if (!Items.Contains(item))
{
Items.Add(item);
item.AddToCategory(this);
}
}
Now that's out of the way, the issues I'm having are:
If I remove the 'inverse="true"' from the Category.Items mapping, I get duplicate entries in the lookup ItemCategory table.
When using 'inverse="true"', I get an error when I try to delete a category as NHibernate doesn't delete the matching record from the lookup table, so fails due to the foreign key constraint.
If I set cascade="all" on the bags, I can delete without error but deleting a Category also deletes all Items in that category.
Is there some fundamental problem with the way I have my mapping set up to allow the many-to-many mapping to work as you would expect?
By 'the way you would expect', I mean that deletes won't delete anything more than the item being deleted and the corresponding lookup values (leaving the item on the other end of the relationship unaffected) and updates to either collection will update the lookup table with correct and non-duplicate values.
Any suggestions would be highly appreciated.
What you need to do in order to have your mappings work as you would expect them to, is to move the inverse="true" from the Category.Items collection to the Item.Categories collection. By doing that you will make NHibernate understand which one is the owning side of the association and that would be the "Category" side.
If you do that, by deleting a Category object it would delete the matching record from the lookup table as you want it to as it is allowed to do so because it is the owning side of the association.
In order to NOT delete the Items that are assigned to a Category object that is to be deleted you need to leave have the cascade attribe as: cascade="save-update".
cascade="all" will delete the items that are associated with the deleted Category object.
A side effect though would be that deleting the entity on the side where the inverse=tru exists will thow a foreign key violation exception as the entry in the association table is not cleared.
A solution that will have your mappings work exactly as you want them to work (by the description you provided in your question) would be to explicitly map the association table.
Your mappings should look like that:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="..."
namespace="...">
<class name="Category" lazy="true">
<id name="CategoryId" unsaved-value="0">
<generator class="native" />
</id>
<property name="Name" />
<bag name="ItemCategories" generic="true" inverse="true" lazy="true" cascade="none">
<key column="CategoryId"/>
<one-to-many class="ItemCategory"/>
</bag>
<bag name="Items" table="ItemCategory" cascade="save-update" generic="true">
<key column="CategoryId"></key>
<many-to-many class="Item" column="ItemId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="..."
namespace="...">
<class name="Item" table="Item" lazy="true">
<id name="ItemId" unsaved-value="0">
<generator class="native" />
</id>
<property name="Name" />
<bag name="ItemCategories" generic="true" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="ItemId"/>
<one-to-many class="ItemCategory"/>
</bag>
<bag name="Categories" table="ItemCategory" inverse="true" cascade="save-update" generic="true">
<key column="ItemId"></key>
<many-to-many class="Category" column="CategoryId"></many-to-many>
</bag>
</class>
</hibernate-mapping>
As it is above it allows you the following:
Delete a Category and only delete the entry in the association table without deleting any of the Items
Delete an Item and only delete the entry in the association table without deleting any of the Categories
Save with Cascades from only the Category side by populating the Category.Items collection and saving the Category.
Since the inverse=true is necessary in the Item.Categories there isn't a way to do cascading save from this side. By populating the Item.Categories collection and then saving the Item objec you will get an insert to the Item table and an insert to the Category table but no insert to the association table. I guess this is how NHibernate works and I haven't yet found a way around it.
All the above are tested with unit tests.
You will need to create the ItemCategory class mapping file and class for the above to work.
Are you keeping the collections in synch? Hibernate expects you, I believe, to have a correct object graph; if you delete an entry from Item.Categories, I think you have to delete the same entry from Category.Items so that the two collections are in sync.

Fetching only base type objects in nHibernate

I'm currently trying to solve a problem. I have a class table inheritance aka table-er-subclass (one main table + several others with additional data). In my app both base object instances and extended objects can exist. Now I want to be able to sometimes fetch only those base objects and sometimes both types. A simple example (both classes are mapped with all of their properties)
public class Base
{
public in ID {get; set;}
public string Something {get; set;}
}
public class Extended : Base
{
public bool NewProp{get; set;}
}
now running hql query "from Base" would fetch both Base and Extedned objects. Is there any way to restrict such behavior to fetch only Base objects?
with HQL you should be able to use the "class" special property:
from Base b where b.class=Base
another approach could be to use plain SQL where you have greater control of what you retrieve.
Anyway check the (N)Hibernate docs.
That's the mapping for the above sample (if there's any error forgive me it must be a typo cause the sample runs)
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping auto-import="true"
default-lazy="false"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:nhibernate-mapping-2.2">
<class name="Base, Test"
table="base">
<id name="ID"
access="property"
column="ID"
type="Int64"
unsaved-value="0">
<generator class="sequence">
<param name="sequence">base_id_seq</param>
</generator>
</id>
<property name="Something"
access="property"
type="String">
<column name="somethin"/>
<joined-subclass name="Extended, Test"
table="extended"
schema="extended">
<key column="id" />
<property name="NewProp"
access="property"
type="Boolean">
<column name="newProp"/>
</property>
</joined-subclass>
</class>
</hibernate-mapping>