I think that the main issue is that the subclass deletes all constraints related to the base mapped collections.
Using the table per concrete class strategy, I have found that the parent collections are not asociated with the subclasses also in another (maybe related) problem, the associations between Basetypes and ChildTypes are not created either.
I have a schema similar to this:
public class Parent{
public virtual Int64 Id{get; set;}
public virtual IList<Foo> foos{get; set;}
public virtual IList<ParentType> _pts{get; set;}
}
public class child: Parent{
public virtual int chilInt{get; set;}
}
public class BaseType{
public virtual Int64 Id{get; set;}
public virtual Parent ParentReference{get; set;}
}
public class ChildType: BaseType{
public virtual string childBacon{get; set;}
}
Mapping Files
<class name="Parent" abstract="true">
<id name="Id" type="Int64" column="Id" unsaved-value="0">
<generator class="native"/>
</id>
<set name="foos" inverse="false" >
<key column="Id"/>
<one-to-many class="Foo" />
</set>
<set name="pts" inverse="false" >
<key column="Id"/>
<one-to-many class="ParentType" />
</set>
</class>
<union-subclass name="Child" table="Child" extends="Parent">
<property name="childInt" type="int" />
</union-subclass>
<class name="ParentType" abstract="true">
<id name="Id" type="Int64" column="Id" unsaved-value="0">
<generator class="native"/>
</id>
<many-to-one name="ParentReference" class="Parent"/>
</class>
<union-subclass name="ChildType" table="ChildType" extends="ParentType">
<property name="childBacon" type="string" />
</union-subclass>
The result that the child table don't have any relation with foo table.
If you use the <union-subclass> mapping it's clear that there is no direct relation of the foo entries to your child table because the child table only contains the additional things declared in the child class.
When instantiating a child instance with union-subclass mapping you get a row in both, the parent and child tables. And if your child instance contains entries in the foo set, you get some rows in the foo table with relation to the parent table.
Using table per concrete class mapping does not make sense with associations pointing to the parent class (as the foo class not part of your code example seems to do) because then the different derived classes of parent all inherit the foo set but the foo table cannot have foreign keys to all those tables.
Well, there are three common approaches for ORM and inheritance (table per class hierarchy, table per subclass, table per concrete class). <union-subclass>, one you use, is used in table per concrete class and it should be embedded in parent <class>. Read about it here (8.1.5).
Maybe it won't resolve all your issues, but at least it should help with establishing mapping for inheritance.
Related
I am trying to use NHibernate with an existing database. The database records unidirectional relationships between users:
Users
UserId PK
Name
Relationships
RelationshipId PK
ParentId FK:Users_UserId
ChildId FK:Users_UserId
I want to represent this using NHibernate. At the moment, I have the following POCO object:
class User {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public ICollection<User> ParentUsers {get; set;}
public ICollection<User> ChildUsers {get; set;}
}
I also have this mapping file:
<class name="User" table="Users">
<id name="Id"></id>
<property name="Name"></property>
</class>
I've looked at guides on the web, but I can't work out what I need to put in the mapping file to wire up my two ICollection properties.
How should I map this data structure? Is my current approach correct, or is it better to create a second POCO class, and just use two many-to-one relationships?
I'm probably missing something, but this should get you started:
<idbag name="ParentUsers" table="Relationships">
<collection-id column="RelationshipId" type="...">
<generator class="..."/>
</collection-id>
<key column="ChildId"/>
<many-to-many column="ParentId" class="User"/>
</idbag>
<idbag name="ChildUsers" table="Relationships">
<collection-id column="RelationshipId" type="...">
<generator class="..."/>
</collection-id>
<key column="ParentId"/>
<many-to-many column="ChildId" class="User"/>
</idbag>
Also, one of the collections should be marked as inverse.
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
class FooBase{...}
class FooDerived : FooBase {...}
class BaseContainer
{
public virtual FooBase Foo {get;set;}
}
class DerivedContainer : BaseContainer
{
public virtual new FooDerived Foo {get;set;}
}
Hibernate mapping options
Option 1 below
Fails to persist on a/c of NHibernate generating additional member declaration in the xml (index out of range error)
<class name="BaseContainer" discriminator-value="0">
<discriminator column="ContainerType" type="int" />
<many-to-one name="Foo"
foreign-key="..."
class="FooBase"
column="FooId"
unique="true"/>
<subclass name="DerivedContainer" discriminator-value="1">
<many-to-one name="Foo"
foreign-key="..."
class="FooDerived"
column="FooId"
unique="true"/>
</subclass>
</class>
Option 2 independent mappings !
Fetch operation erroneous, does not discriminate the types
<class name="BaseContainer" discriminator-value="0">
<discriminator column="ContainerType" type="int" />
<many-to-one name="Foo"
foreign-key="..."
class="FooBase"
column="FooId"
unique="true"/>
</class>
<class name="DerivedContainer" discriminator-value="1">
<many-to-one name="Foo"
foreign-key="..."
class="FooDerived"
column="FooId"
unique="true"/>
</class>
Stuck, would be grateful for any pointers, although I understand this can easily achieved if done via table per subclass, is there any way above can be achieved via table per class hierarchy
I'm having one self-referencing class. A child has a reference to its parent and a parent has a list of children. Since the list of children is ordered, I'm trying to map the relation using NHibernate's .
This is my mapping:
<class name="MyClass">
<id name="Id">
<generator class="native"/>
</id>
<list name="Children" cascade="delete" inverse="true">
<key column="ParentId"/>
<index column="ListOrder"/>
<one-to-many class="MyClass"/>
</list>
<many-to-one name="Parent" class="MyClass" column="ParentId"/>
</class>
The problem I'm having is when having a bi-directional mapping child<->parent, the list index (ListOrder) isn't updated in the database when I do my CRUD dance. This means that when I e.g. remove a child, I get holes in the children list after saving to the database and fetching the parent again. If I remove the bidirectionality, by not having a many-to-one from the children to the parent (and no inverse=true), the ListOrder is updated correctly.
Have any of you seen this before? Is there any simple solution?
Yes, it's because of inverse=true, an alternate solution would be to use a set or bag instead of list with order="ListOrder", add the ListOrder column as a property to the MyClass class with an empty setter and a getter that always returns it's index from it's parent's child collection. Like this:
<class name="MyClass">
<id name="Id">
<generator class="native"/>
</id>
<bag name="Children" cascade="delete" inverse="true" order-by="ListOrder">
<key column="ParentId"/>
<one-to-many class="MyClass"/>
</bag>
<property name="ListOrder" column="ListOrder"/>
<many-to-one name="Parent" class="MyClass" column="ParentId"/>
</class>
and the class
public class MyClass
{
public virtual int ID { get; set; }
public virtual IList<MyClass> Children { get; set; }
public virtual MyClass Parent { get; set; }
public virtual int ListOrder
{
get
{
if (Parent == null || !Parent.Children.Contains(this)) return -1;
return Parent.Children.IndexOf(this);
}
set { }
}
}
Lets say we have an Employee entity composed of a few other entities, such as a one-to-many addresses and contacts, and a few fields (name, age, etc). We mapped this entity out and can use it just fine, saving each piece out into "Employee", "EmployeeAddresses", and "EmployeeContacts" tables.
However, we use pretty much all of this employee's information for a big calculation and have a separate "EmployeeInput" object composed of the same Address and Contact object lists (i.e. both the Employee and EmployeeInputs object has a list of Address and Contact entities). We need to save of this information when we preform the calculation for later auditing purposes. We'd like to save this EmployeeInput entity to an "EmployeeInput" table in the database.
The problem we're running into is how to save the Address and Contact lists? We'd like to stick them into something like "EmployeeInputAddresses" and "EmployeeInputContacts", but the Address and Contact entites are already mapped to "EmployeeAddresses" and "EmployeeContacts", respectively.
What's the easiest way to accomplish this without creating a new "EmployeeInputAddress" and "EmployeeInputContact" entity and separate mapping files for each (as the fields would literally be duplicated one by one). Put another way, how can we map a single entity, Address, to two different tables depending on the parent object it belongs to (EmployeeAddresses table if it's saving from an Employee object, and EmployeeInputAddresses table if it's saving from an EmployeeInput object).
The easiest way would be to have addresses and contacts mapped as composite elements. That way you could map your collection differently for Employee and for EmployeeInput since the mapping is owned by the container.
For example:
public class Employee
{
public List<Address> Addresses{get; set;}
}
public class EmployeeInput
{
public List<Address> Addresses{get; set;}
}
public class Address
{
public string Street{get;set;}
public string City{get; set;}
}
Would have the folloying mapping:
<class name="Employee" table="Employees">
<id name="id">
<generator class="native"/?
</id>
<list name="Addresses" table="EmployesAddresses">
<key column="Id" />
<index column="Item_Index" />
<composite-element class="Address">
<property name="Street" />
<property name="City" />
</composite-element>
</list>
</class>
<class name="EmployeeInput" table="EmployeesInput">
<id name="id">
<generator class="native"/?
</id>
<list name="Addresses" table="EmployeesInputAddresses">
<key column="Id" />
<index column="Item_Index" />
<composite-element class="Address">
<property name="Street" />
<property name="City" />
</composite-element>
</list>
</class>