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>
Related
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>
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.
I've been wrecking my mind on how to get my tagging of entities to
work. I'll get right into some database structuring:
tblTag
TagId - int32 - PK
Name
tblTagEntity
TagId - PK
EntityId - PK
EntityType - string - PK
tblImage
ImageId - int32 - PK
tblBlog
BlogId - int32 - PK
class Image
Id
EntityType { get { return "MyNamespace.Entities.Image"; }
IList<Tag> Tags;
class Blog
Id
EntityType { get { return "MyNamespace.Entities.Blog"; }
IList<Tag> Tags;
The obvious problem I have here is that EntityType is an identifer but
doesn't exist in the database. If anyone could help with the this
mapping I'd be very grateful.
You don't need the entity type. Take a look at any-type mapping (it stores the type name in the database in the relation table, but you don't need it in the entity model).
See this blog post by ayende.
Edit: tried to write an example.
You could have an own table for each tagged object, this is easy and straight forward, you don't even need any types:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="Image_Tags">
<key column="Image_FK"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
Tried to use some advanced features to map it into a single table, but I think it doesn't work this way:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
<bag name="Objects" table="tblTagEntity" access="noop">
<key column="TagId"/>
<many-to-any id-type="System.Int64" meta-type="System.String">
<meta-value
value="IMAGE"
class="Image"/>
<meta-value
value="BLOG"
class="Blog"/>
<column name="EntityType"/>
<column name="EntityId"/>
</many-to-any>
</bag>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="tblTagEntity" where="EntityType='IMAGE'">
<key column="EntityId"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
The tricks here are:
access="noop" to specify the foreign key without having a property in the entity model, see this post.
where="EntityType='IMAGE'" to filter the loaded data.
The problem is that most probably the EntityType is not set to any useful value. This could be fixed somewhere, but I don't think that it is worth the effort.
Someone else has probably a better idea.
Edit 2: another (working) solution
make the association table an entity:
in short:
Tag => TagEntity: not mapped or one-to-many inverse (noop)
TagEntity => Tag: many-to-one
TagEntity => Object: any
Object => TagEntity: one-to-many inverse
This should work straight forward.
classes:
class Tag
{
string Name { get; set; }
}
class TagEntity
{
Tag Tag { get; set; }
object Entity { get; set; }
}
class Image
{
IList<TagEntity> tags { get; private set; }
}
The only drawback seems to be that you have to make sure that the bidirectional associations are consistent without loading to much data. Note that inverse collections are not stored.
Edit 2: Performance notes
When you add / remove tags, you could do a trick. TagEntity has a reference to the tagged entity. The Entity also has a list of TagEntities, but this is marked as inverse. (This means, they are loaded, but not stored.)
You can add and remove tags without loading the Entity an without loading all the tags.
Adding:
Get Tag to add (or load proxy if you have the id of the tag)
Load Entity (just proxy, using session.Load, no db access here)
create new TagEntity, assign tag and entity-proxy
save TagEntity
Removing:
Get TagEntity to remove
delete TagEntity.
Within the session, you don't have this tag assigned to/removed from the TagEntity. This works fine assumed that you only add or remove tags within this transaction.
I you define a list of TagEntities on the Tag, you can do the same, without loading all the TagEntities just to add or remove one.
You could make EntityType an Enum in your code. And/or, you could try making EntityType an actual entity in your database (tblEntityType).
Got Stefans final solution to work! Here's my final mappings:
Image
<bag name="TagEntites" table="tblTagEntity" cascade="all" fetch="join" inverse="true" where="EntityType='EntityImage'">
<key column="EntityId"></key>
<one-to-many class="TagEntity" />
</bag>
TagEntity
<id name="Id">
<column name="TagEntityId"></column>
<generator class="identity" />
</id>
<any name="Entity" id-type="System.Int32" meta-type="System.String">
<meta-value value="EntityImage" class="Image" />
<column name="EntityType"></column>
<column name="EntityId"></column>
</any>
<many-to-one name="Tag" class="Tag" cascade="all" fetch="join">
<column name="TagId"></column>
</many-to-one>
I started using NHibernate this week (struggling). I have a small application with 3 tables (I use 2 for now). Table currency and table country here are the mapping files.
<class name="dataprovider.Country,dataprovider" table="country">
<id name="CountryId" column="country_id" unsaved-value="0">
<generator class="native"/>
</id>
<!--<bag name="BatchList" inverse="true" lazy="true" >
<key column="country_id" />
<one-to-many class="Batch" />
</bag>
<bag name="PrinterList" inverse="true" lazy="true" >
<key column="country_id" />
<one-to-many class="Printer" />
</bag>-->
<many-to-one name="CurrencyId" column="currency_id" class="Currency" cascade="save-update"/>
<!--<property column="currency_id" name="Currency_Id"/>-->
<property column="name" name="Name"/>
<property column="region" name="Region" />
<property column="comments" name="Comments"/>
</class>
The currency mapping file:
<class name="dataprovider.Currency, dataprovider" table="currency">
<id name="CurrencyId" column="currency_id" >
<generator class="native"/>
</id>
<bag name="CountryList" inverse="true" lazy="true" >
<key column="currency_id" />
<one-to-many class="Country" />
</bag>
<!--<bag name="DenominationList" inverse="true" lazy="true" >
<key column="currency_id" />
<one-to-many class="Denomination" />
</bag>-->
<property column="name" name="Name"/>
<property column="authorizer" name="Authorizer" />
<property column="date_created" name="DateCreated" type="DateTime" not-null="true" />
<property column="comments" name="Comments" />
The many to one relationship that country hold create an attribute of the type Currency in the country persistence class. Now while my test can_add_currency and can_add_country succeeded (I export the schema) I have null value in the table country on the field currency_id.
Here is the test code:
[Test]
public void can_add_new_country()
{
CountryManager cm = new CountryManager();
Country country = new Country();
//country.CurrencyId = CurrencyManager.GetCurrencyById(1);
country.CurrencyId = new CurrencyManager().GetCurrencyById(1);
country.Name = "POUND";
country.Region = "ENGLAND";
country.Comments = "the comment";
cm.Add(country);
using(ISession session = _sessionFactory.OpenSession())
{
Country fromdb = session.Get<Country>(country.CountryId);
Assert.IsNotNull(fromdb);
Assert.AreNotSame(fromdb, country);
}
}
public Currency GetCurrencyById(int currency_id)
{//GetCurrencyById from CurrencyManger class
try
{
using(ISession session = NHibernateHelper.OpenSession())
{
return session.Get<Currency>(currency_id);
}
} catch (Exception ex)
{
return null;
}
}
The question is: how to insert into table country with the currency_id of an existing currency_id from the table currency?
How do you guys/gals do it? I'm seriously stuck and a 2 day small project is taking me one week now.
Set cascade="save-update" on your bag name="CountryList". If that doesn't work, it may be helpful to post your code for CountryManager.Add() to see how the saving is taking place.
In response to your second question, if I understand it correctly, this is how NHibernate treats mapped collections:
You mapped the collection as lazy, so loading the object will not load all the elements of the collection at the same time. Instead, when you first access the collection NHibernate will query the database to fill the collection and return it. So, when you first do something like:
var countries = currency.CountryList;
or
foreach (Country country in currency.CountryList)
NHibernate will silently execute a query similar to:
SELECT * FROM country WHERE currency_id = ?
And then build a collection of Country objects to return (and cache so that the query is not run again).
Basically, through the mapping file you've already told NHibernate all about your two entities (Country and Currency) and how they are related so it knows how to build the queries to access the data. Similarly, it keeps track of what was in the collection so when you add or remove items, it can compare what was changed and execute the appropriate INSERT or REMOVE statements.
So, the way to use collections mapped by NHibernate is to use them just as you would with a normal .NET collection. Add and remove items at your will. Just when you are finished, make sure you tell NHibernate to persist the changes you made to the database, either by calling session.Save() or session.Delete() on every item you add/remove or (if you have set cascading, like you have) simple call session.Save() on the parent object that contains the collection.
My cascade was rather on the this side:
<many-to-one name="CurrencyId" column="currency_id" class="Currency" cascade="save-update"/>
After I changed it it wasn't working at first even though I rebuild the solution. Then I did another test from the Currency_Test class: can_get_currency_by_id which call the same function GetCurrncyById and I could have an object while from can_add_new_country the same function returns null.
Then I realise that the ExportSchema of either Country_Test [Setup] or Currency_Test is not creating the database on time for the can_add_new_product to have a currency object. That's why it's returning null object.
Now not to abuse; but can you tell me how to use IList<Counrty>CountryList? I don't know if I put it well. From my understanding it should store all country objects using the same currency (currency_id reference). How does NHibernate do that?
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>