My database structure looks something like this:
Person
Id
Name
FieldA
FieldB
Phone
Id
Number
PersonPhone
PhoneId
PersonId
IsDefault
My NHibernate mappings for the Person and Phone objects are straight forward, its the PersonPhone I'm having difficult with. I want to have a collection of PersonPhone objects as a property of Person which will allow me to have the Phone number of a person and be able to tell which is the "default" or primary phone number for a person.
ideally Id like my PersonPhone object to look like this:
public class PersonPhone
{
public virtual Person Person { get; set; }
public virtual Phone Phone { get; set; }
public virtual bool IsDefault { get; set; }
}
so far my NHibernate mapping for this table looks like the following:
<class name="PersonPhone" table="PersonPhone">
<composite-id>
<key-property name="Person" column="PersonId" />
<key-property name="Phone" column="PhoneId" />
</composite-id>
<property name="IsDefault" column="IsDefault"/>
</class>
but when NHibernate compiles my mappings I get an error saying:
Could not compile the mapping document: MyApp.Entities.PersonPhone.hbm.xml. NHibernate.MappingException : Could not determine type for: MyApp.Entities.Person, MyApp.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(PersonId)
Any ideas on how this should be mapped?
The answer is to use the element in your composite key rather than the key-property
<class name="PersonPhone" table="PersonPhone">
<composite-id>
<key-many-to-one name="Person" column="PersonId"></key-many-to-one>
<key-many-to-one name="Phone" column="PhoneId"></key-many-to-one>
</composite-id>
<property name="IsDefault" column="IsDefault"/>
</class>
I think It is more proper to consider Many-to-Many relationship between Phone and Peron entities and get rid of PersonPhone entity.
To set-up the same mapping with Fluent NHibernate, do this:
public class PersonPhoneMap : ClassMap<PersonPhone>
{
public PersonPhoneMap()
{
CompositeId()
.KeyReference(p => m.Person)
.KeyReference(p => m.Phone);
References(p => p.Person)
.Column("PersonID");
References(m => m.Phone)
.Column("PhoneID");
Map(p => p.IsDefault)
.Column("IsDefault");
}
}
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'm using NHibernate 3.2 and have two tables mapped for same class, specifying the "entity-name" in mapping. The trouble is that when I use the method in ISession to indicate the entity name NHibernate insists deduct on their own behalf, ignoring my specification.
This is code from my unit tests:
public class Cliente
{
public virtual Guid UID { get; set; }
public virtual long Revisao { get; set; }
public virtual string Nome { get; set; }
public virtual DateTime DataNascimento { get; set; }
}
<class name="Cliente">
<id name="UID">
<generator class="guid"/>
</id>
<version name="Revisao" />
<property name="Nome" />
<property name="DataNascimento" />
</class>
<class name="Cliente" entity-name="ClienteAudit" schema="audit">
<composite-id>
<key-property name="UID" />
<key-property name="Revisao" />
</composite-id>
<property name="Nome" />
<property name="DataNascimento" />
</class>
var cliente = new Cliente {DataNascimento = DateTime.Parse("1988/07/09"), Nome = "Heber Senger"};
using (var ss = sf.OpenSession())
{
ss.Save("Cliente", cliente);
ss.Flush();
}
NHibernate insists in save the entity as "ClienteAudit" (I verify in listener and table), and I explicity inform entity name as "Cliente".
I just try:
- Specify entity name in Cliente mapping;
- Omit name in method save, let NHibernate free to discover the name, implying in "ClienteAudit" again;
- Now I studying internal code of SessionImpl and so on.
If anyone can help would be great. Thanks.
Two changes were needed to all work:
Default type of version property is int and NOT long;
And the most important: the name specified in save method is the full name of class when entity-name don't was explicity indicated in HBM.
By the way, thanks!
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.
I'm new to nhibernate, and I'm sorry if this is answered elsewhere, but I've been looking for the last couple of hours, and can't find a solution that works.
A bit of background:
I'm trying to write an Admin area where there are users and sites, and a user can have access to multiple sites - but at various permission levels for each site.
Ideally I would like my classes look like this.
namespace MyApp.Users
{
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Password { get; set; }
public virtual IList<AdminUserSite> Sites { get; set; }
}
public class AdminUserSite
{
public virtual int UserTypeId { get; set; }
public virtual Site AdminSite { get; set; }
public virtual IList<Permission> Permissions { get; set; }
}
public class Permission
{
public virtual int Id { get; set; }
public virtual int AreaID { get; set; }
public virtual bool CanView { get; set }
public virtual bool CanEdit { get; set }
}
}
namespace MyApp.Sites
{
public class Site
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
}
}
And my database schema looks like this
f_user
{
f_user_id (int, PK)
name (nvarchar(50))
password (nvarchar(25))
}
f_user_site
{
f_user_id (int, PK)
f_site_id (int, PK)
d_user_type_id (int)
}
f_perm
{
f_perm_id (int, PK)
f_site_id (int)
f_user_id (int)
d_area_id (int)
can_read (bit)
can_write (bit)
}
f_site
{
f_site_id (int, PK)
title (nvarchar(50))
}
And the hibernate mapping files currently look like:
Users.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MyApp"
namespace="MyApp.Users"
default-lazy="true">
<class name="User" table="f_user">
<id name="Id" column="f_user_id">
<generator class="identity" />
</id>
<property name="Name" column="name" />
<property name="Password" column="password" />
<bag name="Sites" table="f_user_site" inverse="true" cascade="all-delete-orphan">
<key column="f_user_id"/>
<one-to-many class="AdminUserSite"/>
</bag>
</class>
<class name="Permission" table="f_perm">
<id name="Id" column="f_perm_id">
<generator class="identity" />
</id>
<property name="AreaId" column="d_area_id" />
<property name="CanView" column="can_read" />
<property name="CanEdit" column="can_write" />
</class>
<class name="AdminUserSite" table="f_user_site">
<property name="UserTypeId" column="d_user_type_id" />
<many-to-one name="Site" class="MyApp.Sites.Site, MyApp.Sites" foreign-key="f_site_id"></many-to-one>
</class>
</hibernate-mapping>
and Sites.hbm.xml is
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MyApp"
namespace="MyApp.Sites"
default-lazy="true">
<class name="Site" table="f_site">
<id name="Id" column="f_site_id">
<generator class="identity" />
</id>
<property name="Title" column="title" />
</class>
</hibernate-mapping>
Individually the User, Permission and Site classes all map fine - but i just can't figure out what AdminUserSite should be, and I haven't even attempted to put the permissions list in there yet.
Does anyone have any ideas?
Any help would be very appreciated.
Saan
I think you need to work on the database schema a bit. I think the AdminUserSite table is your problem.
Firstly, work out your entities (these should be simple nouns, which is why AdminUserSite seems out of place to me).
Entities: User, Permission, Site, SiteArea
Next, work out the relationships:
1 User has many Permissions
1 SiteArea has many Permissions
1 Site has many SiteAreas
(hope I got that right :) )
After that, your hbm and tables should flow more naturally.
Remember that normally you will have 1 table per entity, unless you have a many-many relationship (in which case you will need a joining table).
Generally a relation table mapping look like this :
<class name="AdminUserSite" table="f_user_site">
<composite-id >
<key-many-to-one name="Site" column="f_site_id" class="Site" />
<key-many-to-one name="User" column="f_user_id" class="User"/>
</composite-id>
</class>
In that way you can access either Site or User by its relation object or load relation object by criteria based on Site or User.
By the way, if your Permission object contains reference to user and site you may not need the AdminUserSite relation, the permission mapping already do it.
Edit about your comment Kind of replicating the same information in two spots.
Since NHibernate -like all ORM- has a first level cache you don't have to matter if your object can be accessed in two ways. You just have to ensure that your mappings are usefull to optimize and design your application well.
In this case, it's not a 'replication' but a reference. The object will be the same in the two spots if loaded by/retrieved from the same NHibernate session.
Accessing an object in two ways if they have a logic (ie. direct way and cross-relation way) is not an heresy.
This is most a data layer work to achieve to provide right methods to access the right objects in the right way :)