I have a problem in NHibernate mapping.
I have Class Company, Person and Address; Company and Person both can have Addresses hence i have taken Address in both. To store this I have tables Company, Person and Address. Now Company will have Address object and Person will also have Address Object so Address should also have reference to Company and Person object. So I created two child classes of Address 1. CompanyAddress 2. PersonAddress and in Database I created two more tables Company_Address and Person_Address. Now In Address.hbm.xml i have added Joined subclass for both CompanyAddress and PersonAddress which are referring to Company_Address and Person_Address tables respectively.
Now CompanyAddress class is having company object in it and PersonAddress class is having Person object in it.
Company_Address is having 2 columns AddressId(PK) and CompanyId(FK)->Company
Person_Address is having 2 columns AddressId(PK) and PersonId(FK)->Person
I have created one-to-one mapping in Company.hbm.xml for Address.
When i Save Company object every table is populating properly except Company_Address.
AddressId is getting stored but CompanyId is not getting stored.
I have no idea how to get this working
If someone can faced this problem please help.
Thanks in advance!!!
Pawan Shukla
It sounds like you may have over-normalized here, given that you have set up one-to-one mappings. What might be easier (and WAY cleaner in code) is to place your address fields in the Company and Person tables themselves, then set up a simple address object and treat it as a component. Here's my address class:
public class StreetAddress
{
public string CountryCode { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string County { get; set; }
public string StateCode { get; set; }
public string PostalCode { get; set; }
public StreetAddress()
{
// Constructor for NHibernate
}
public StreetAddress(string countryCode, string street, string city, string county, string stateCode, string postalCode)
{
CountryCode = countryCode;
Street = street;
City = city;
County = county;
StateCode = stateCode;
PostalCode = postalCode;
}
}
Then you treat the address as a component, and map it like this:
<component name="Address" insert="true" update="true" optimistic-lock="true">
<property name="CountryCode">
<column name="Address_CountryCode" />
</property>
<property name="Street">
<column name="Address_Street" />
</property>
<property name="City">
<column name="Address_City" />
</property>
<property name="County">
<column name="Address_County" />
</property>
<property name="StateCode">
<column name="Address_StateCode" />
</property>
<property name="PostalCode">
<column name="Address_PostalCode" />
</property>
</component>
Don't do it this way. Address is clearly not an entity (and hence has no table of it's own and no primary key). What I would rather do is to model it as a component. A sample mapping might look like this:
<class name="Company"
table="Company">
<id name="Id">
<generator class="identity"/>
</id>
<property name="CompanyName" />
<component name="Address">
<property name="Street"/>
<property name="HouseNumber"/>
<property name="City"/>
<property name="PostOffice"/>
</component>
</class>
Just google a bit. In DDD there's a notion of a value object opposed to an entity, and the way to model value objects in NHibernate is to use components.
Related
I am trying to get NHibernate to save complete object graph in a one-to-one mapping scenario. I have following classes
public class Employee : EntityBase
{
public virtual string EmployeeNumber { get; set; }
public virtual Address ResidentialAddress {get; set; }
}
public class Address : EntityBase
{
public virtual string AddressLine1 { get; set; }
public virtual string AddressLine2 { get; set; }
public virtual string Postcode { get; set; }
public virtual string City { get; set; }
public virtual string Country { get; set; }
public virtual Employee Employee { get; set; }
}
I am trying to use one-to-one mapping here so that one employee has only one residential address. My mappings are below
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
<class name="Employee">
<id name="Id" generator="hilo" />
<property name="EmployeeNumber" length="10" />
<one-to-one name="ResidentialAddress" class="Address" property-ref="Employee" cascade="all" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
<class name="Address">
<id name="Id" generator="hilo" />
<property name="AddressLine1"/>
<property name="AddressLine2" />
<property name="Postcode" />
<property name="City" />
<property name="Country" />
<many-to-one name="Employee" class="Employee" unique="true" />
</class>
</hibernate-mapping>
I am using following code to save an instance of employee object
using (var transaction = Session.BeginTransaction())
{
id = Session.Save(new Employee
{
EmployeeNumber = "123456789",
ResidentialAddress = new Address
{
AddressLine1 = "Address line 1",
AddressLine2 = "Address line 2",
Postcode = "postcode",
City = "city",
Country = "country"
}
});
transaction.Commit();
}
In the above situation, the foreign key on Address back to Employee is always null. But if I change RResidentialAddress property on Employee class so that Employee property is always populated correctly as below
private Address address;
public virtual Address ResidentialAddress
{
get { return address; }
set
{
address = value;
if (value != null) value.Employee = this;
}
}
This makes it work perfectly. Why do I have to set ResidentialAddress.Employee? Am I missing something in the mappings? Should NHibernate not automatically save the complete object graph (and hence determine proper foreign key values).
The above working code concerns me as it may create a problem when called from NHiberante during loading of entity from database.
Your are not missing anything.
We (developers) should always care about setting both ends of any bidirectional relation.
When we use one-to-many (without inverse="true") and without setting the many-to-one, we always end up with redundant and unefficient sequence of SQL statements:
INSERT child record, place NULL into Parent_ID
UPDATE child record, set the Parent_ID with Parent.ID
This is not suggested: 6.8. Bidirectional Associations:
... The non-inverse side is used to save the in-memory representation to the database. We would get an unneccessary INSERT/UPDATE and probably even a foreign key violation if both would trigger changes! The same is of course also true for bidirectional one-to-many associations...
So, that is in one-to-many, many-to-one.
In case of one-to-one
There is no collection persister in place as discussed above. No man in the middle, which would take care about the other end, and issue "unefficient" INSERT and later UPDATE. NHibernate will use standard entity persisters for Employee and Address.
Each association end of the relation (Employee-Address) has its own persister. These could be triggered in a cascade (usually good idea to have <one-to-one ... cascade="all" />)
But each persister needs enough information to create proper INSERT or UPDATE statement. I.e. even C# Address must know about its Employee. The Address persister, will handle INSERT/UPDATE alone, with access to Address instance only.
SUMMARY: We have to set both ends in code.... And that is good practice even if we are not forced (non inverse child mapping with nullable parent column).
BTW: once loaded from DB, we expect that NHibernate will set both ends... why should not we do the same?
I am trying to map a relaitively simple data model with NHibernate for use with breeze.js. The data model consists of four entities and looks like this:
The problem seems to be with the relationship between the CourseDate and the CourseDateStudent entity. When calling the Metdata method of my BreezeController I get the following error:
System.ArgumentException: "Could not find matching fk for property HR.CourseManager.Web.Data.Entities.CourseDateStudent.CourseDate"
I do not know what exactly causes this behavior as I think I have implemented my model as recommended by the breeze.js documentation.
There is another question convering a somewhat similar topic, but this question does not address having composite keys.
(Remark: Adding surrogate keys like numeric ids is not an option here. As you might have guessed this is not the real data model but one that simply has had its entities renamed. The underlying database is used by a whole bunch of applications and cannot be changed.)
Enough of writing about my problem. Let's see some code! So there is the CourseDate entity which has a composite key consisting of the properties CourseCode and Date:
CourseDate.cs:
public class CourseDate {
public CourseDate() {
this.Students = new List<CourseDateStudent>();
}
public virtual Course Course { get; set; }
public virtual DateTime Date { get; set; }
public virtual string CourseCode { get; set; }
public virtual ICollection<CourseDateStudent> Students { get; set; }
// Skipped implementation of Equals and GetHashCode for readability
}
CourseDate.hbm.xml:
<hibernate-mapping assembly="CourseManager.Web"
namespace="CourseManager.Web.Data.Entities"
xmlns="urn:nhibernate-mapping-2.2">
<class name="CourseDate" table="COURSE_DT" lazy="true" >
<composite-id>
<key-many-to-one name="Course" column="COURSE_CODE" />
<key-property name="Date" column="DT" />
</composite-id>
<property name="CourseCode" insert="false" update="false">
<column name="COURSE_CODE" sql-type="VARCHAR2" not-null="true" />
</property>
<bag name="Students" inverse="true">
<key>
<column name="COURSE_CODE" />
<column name="COURSE_DT" />
</key>
<one-to-many class="CourseDateStudent" />
</bag>
</class>
</hibernate-mapping>
And then there is the CourseDateStudent entity that has a primary key consisting of the CourseCode and the CourseDate property (thus defining the foreign key relationship to the CourseDate entity) as well as the StudentId property:
CourseDateStudent.cs
public class CourseDateStudent {
public virtual CourseDate CourseDate { get; set; }
public virtual Student Student { get; set; }
public virtual long StudentId { get; set; }
public virtual string CourseCode { get; set; }
public virtual DateTime Date { get; set; }
// Skipped implementation of Equals and GetHashCode for readability
}
CourseDateStudent.hbm.xml
<hibernate-mapping assembly="CourseManager.Web"
namespace="CourseManager.Web.Data.Entities"
xmlns="urn:nhibernate-mapping-2.2">
<class name="CourseDateStudent" table="COURSE_DT_STUD_LNK" lazy="true" >
<composite-id>
<key-many-to-one name="CourseDate">
<column name="COURSE_CODE" />
<column name="COURSE_DT" />
</key-many-to-one>
<key-many-to-one name="Student" column="STUD_ID" />
</composite-id>
<property name="StudentId" insert="false" update="false">
<column name="STUD_ID" sql-type="NUMBER" />
</property>
<property name="CourseCode" insert="false" update="false">
<column name="COURSE_CODE" sql-type="VARCHAR2" not-null="true" />
</property>
<property name="Date" insert="false" update="false">
<column name="COURSE_DT" sql-type="DATETIME" not-null="true" />
</property>
</class>
</hibernate-mapping>
There was a bug in the Breeze code that builds metadata from NHibernate models. As you discovered, it wasn't finding the foreign keys correctly when composite keys were used.
The fix for the issue is now on github and will be in the next release. Sorry it took so long!
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.
I have this Address table like below:
Address
-------
ID (pk)
OWNERTYPE (int)
OWNERID (int)
ADDR1
ADDR2
....
this is a "child" table, where it would be mapped to any possible "parent" table. To discriminate which parent table it has relation to, it has the column "ONWERTYPE". "OWNERTYPE" will store the identifier which parent table it relates to, while "OWNERID" will hold the primary key of the parent table (this is Foreign key column).
how do i map this relation with the parent table?
p/s:
the parent table do not have any
column that indicates its relation to
the child.
the parent tables have their own entities that represent them
You need to use an <any> mapping, as explained here.
I have found another solution but I need advice whether I'm doing the correct way.
The database tables itself is not design by me, thus I cant help but to follow the structure.
because all of the address data is stored only in one table, I use an inheritance mapping to do this.
for example i have 3 tables like this;
Address table
Address
-------
ID (pk)
ownertype (string)
ownerid (int)
addr1
postcode
...
User table
User
-----
ID (pk)
name
...
Org table
Org
----
ID (pk)
Orgname
....
according to my DBA, she said that both User and Org table has relation to the address table, and it is differentiated by the ownertype column in the address table.
so, I made the one base entity "Address" that has all the common properties like this:
public abstract class Address {
public virtual string addr1 { get;set; }
public virtual int postcode { get;set; }
...
}
then for each parent table relation to the address table, I made another subclass of address (derived from class Address) and made so that parent entity relationship to this subclass instead of the Address class, like so:
UserAddress subclass of Address
public class UserAddress : Address {
public virtual User Owner { get;set; }
}
User entity
public class User {
public virtual int ID { get;set; }
public virtual string Name { get;set; }
public virtual UserAddress Address {
get {
return address;
}
set {
if (value != null)
value.Owner = this
address = value
}
}
}
and the mapping for Adderss, UserAddress and user is like the following:
Address and its subclasses (Table per class hierarchy strategy):
<class name="Address" table="Address" abstract="true">
<id name="ID">
<generator class="identity"/>
</id>
<discriminator column="Ownertype" type="System.String" />
<property name="addr1 />
<property name="postcode" />
....
<subclass name="UserAddress" discriminator-value="USER">
<many-to-one name="Owner" column="Ownerid" />
</subclass>
<subclass name="OrgAddress" discriminator-value="ORG">
<many-to-one name="Owner" column="Ownerid" />
</subclass>
</class>
User:
<class name="User" table="User">
<id name="ID">
<generator class="identity"/>
</id>
<property name="Name" />
....
<one-to-one name="Address" property-ref="Owner" cascade="all" />
</class>
is this the correct way to do this? do give me any other better alternative than this.
notes: i do not show here on Org entity and mapping, basically it has the same concept as user table. pardon me for any syntax error as I type this here, because the real entity name is not intuitive without docs to refer.
Whilst I realise that a Set or Bag is probably the correct way to do this, I'm new to NHibernate and I'm trying to understand why the following is happening.
I have two classes:
public class Customer
{
public virtual int Id { get; protected set; }
public virtual string CustomerName { get; set; }
// Customer has many domains
public virtual IList<Domain> Domains { get; set; }
}
public class Domain
{
public virtual int Id { get; protected set; }
public virtual int CustomerID { get; set; }
public virtual string DomainName { get; set; }
}
My mapping files look like:
<!-- Domain -> tblDomains -->
<class name="Domain" table="tblDomains">
<id name="Id">
<column name="DomainID" sql-type="int" not-null="true"/>
<generator class="identity"/>
</id>
<property name="CustomerID"/>
<property name="DomainName"/>
</class>
<!-- Customer -> tblCustomer -->
<class name="Customer" table="tblCustomer">
<id name="Id">
<column name="CustomerID" sql-type="int" not-null="true"/>
<generator class="identity"/>
</id>
<property name="CustomerName" column="Customer"/>
<list name="Domains">
<key column="CustomerID"/>
<index column="DomainID"/>
<one-to-many class="Domain" />
</list>
</class>
When I retrieve a Customer object the Domains property contains a list of 665383 null Domain objects. The 665384'th item in the list contains a valid populated object.
There are only 63 Domain's that belong to this customer so I'm guessing this is some kind of cartesian product result. I've peeked at the SQL in NHProfiler but all I see is a query that looks fairly innocent when I iterate over the first item in the Domains list:
SELECT domains0_.CustomerID as CustomerID1_,
domains0_.DomainID as DomainID1_,
domains0_.DomainID as DomainID2_0_,
domains0_.CustomerID as CustomerID2_0_,
domains0_.DomainName as DomainName2_0_
FROM tblDomains domains0_
WHERE domains0_.CustomerID = 5667 /* #p0 */
If I use a <bag> this all works just fine. Can anyone explain what's going on under the bonnet?
With a list mapping, the index applies to the set of objects in the list. That is, if a Customer has a set of 63 Domains then normally the list would contain values from 0 to 62 to indicate the index of the Domain object in that Customer's set of Domains.
You have set the index to DomainId, the primary key of the table, which is wreaking havoc. I would guess that the Domain table has 665384 rows total (or fewer if DomainId doesn't start with 1 and has gaps), but I would think that 63 would be valid instead of one. Did you check them all? :-)