i'm fairly new to NHibernate and although I'm finding tons of infos on NHibernate mapping on the web, I am too silly to find this piece of information.
So the problem is, i've got the following Model:
this is how I'd like it to look. One clean person that has two Address Properties.
In the database I'd like to persist this in one table.
So the Person row would have a ShippingStreetname and a Streetname Column, the one mapped to ShippingAddress.Streetname and the other to Address.StreetName
I found an article on fluent interfaces, but still haven't figured out how to do this through the XML Configuration.
Thanks in advance!
Update: I found the solution to this by myself. This can be done through the node and works rather straightforward.
To achieve the mapping of Address and ShippingAddress I just had to add the following to the
<component name="Address" class="Address">
<property name="Streetname"></property>
<property name="Zip"></property>
<property name="City"></property>
<property name="Country"></property>
</component>
<component name="ShippingAddress" class="Address">
<property name="Streetname" column="ShippingStreetname" />
<property name="Zip" column="ShippingZip" />
<property name="City" column="ShippingCity" />
<property name="Country" column="ShippingCountry" />
</component>
Ok. I found the solution myself.
The key is the construct in the XML configuration and it works rather nicely.
Here is how it's done:
<component name="Address" class="Address">
<property name="Streetname"></property>
<property name="Zip"></property>
<property name="City"></property>
<property name="Country"></property>
</component>
<component name="ShippingAddress" class="Address">
<property name="Streetname" column="ShippingStreetname" />
<property name="Zip" column="ShippingZip" />
<property name="City" column="ShippingCity" />
<property name="Country" column="ShippingCountry" />
</component>
you could configure this as two relations. e.g.
<many-to-one name="ShippingAddress" class="Yournamespace.Address"/>
<many-to-one name="Address" class="Yournamespace.Address"/>
You dont even need an Id for an address. Just think how expensive is to maintain an Id. You have concurrency problems, you need uniqueness, and so on. This is the aim of the ValueObjects (do not get confused with System.ValueObject see DDD definition for ValueObject). In this case Address is a ValueObject so it does not required an Id. And if you need a collection of Address you map it like a "" see http://www.nhforge.org/doc/nh/en/index.html#collections-ofvalues.
Related
Having trouble with implementing second level cache in Nhibernate. I have a class mapped as follows:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Data" namespace="Data">
<class name="Account" table="Accounts" lazy="false">
<cache region="Standard" usage="read-write" include="all"/>
<id column="ID" name="ID">
<generator class="assigned" />
</id>
<version name="VersionStamp" column="VersionStamp" type="integer" unsaved-value="0" />
<property name="Name" not-null="true" />
<property name="Type" not-null="true" />
<property name="ClientID" not-null="true" insert="false" update="false" />
<property name="DateCreated" not-null="true" type="UtcDateTime" />
<property name="LastUpdatedDate" not-null="true" type="UtcDateTime" />
<property name="IsActive" not-null="true" />
<many-to-one name="Client" class="Client" column="ClientID" not-found="exception" not-null="true" />
</class>
</hibernate-mapping>
The property "ClientID" is a foreign key into the Clients table and the Client many-to-one property uses it to look up the associated client object.
When I add a new Account, I look up the Client object from the database with a Session.Get and assign it to my Account object's Client property. Behind the scenes, this also automatically populates the ClientID property when the object is written to the database, and the ID is correctly stored in the database. When I retrieve the Account object from the database by ID using Session.Get, all the fields are populated correctly when the object is retrieved.
However, when I implement the second level cache using the settings shown above, the ClientID property is NOT populated when the Account object is retrieved using Session.Get, but the Client property is populated correctly. Is there some reason why this will not work with second level cache? Or have I done something wrong in my mapping/configuration?
For now I am just using SysCache as my caching provider, and both query and second level cache are turned on. The Client class mapping contains a corresponding one-to-many property for the Accounts.
I like the convenience of having the ClientID property on my Account class, so that I can read it without using the Client property, and it seems to work fine without caching.
Thanks for any help.
Rich
I tried to reproduce your situation locally. With your mapping (used the same as the snippet above) I was able to get incorrect behaviour only on UPDATE. In that case, the ClientID was cached, and while the Client reference was changed, the ClientID remained unchanged. In other cases caching was working as expected.
The solution is to change the mapping. The below suggested mapping is the most suitable for read-only properties like ClientID. (I am using that approach as well).
<property name="ClientID" formula="[ClientId]"
not-null="true" insert="false" update="false" />
So the trick is in the formula mapping instead of Column (the default when none is provided)
I have a class mapped to a view and am searching on first and last names in order to search for patient records. The view ultimately looks at the first and last name fields on the patient table (possibly others as well depending on input). When the criteria converts to SQL, it's entering my strings as nvarchar parameters. I've already used type="AnsiString" and length="50" on my mapping, but it still is converting these to nvarchar, which is causing a performance hit on my query.
<class name="PatientSearchResult" table="vw_PatientSearch" mutable="false" >
<id name="Id" type="Guid" column="PatientId"/>
<property name="MedicalRecordNumber" type="AnsiString" length="50" />
<property name="Title" />
<property name="FirstName" type="AnsiString" length="50" />
<property name="MiddleName" />
<property name="LastName" type="AnsiString" length="50" />
<property name="Nickname" />
<property name="Suffix" />
<property name="DateOfBirth" />
<property name="IsRestricted" />
<property name="IsDeleted" />
<component name="Address">
<property name="StreetAddress1" />
<property name="StreetAddress2" />
<property name="City" />
<property name="State" />
<property name="PostalCode" />
</component>
</class>
SQL Profiler is showing the output SQL as using nvarchar parameters and prefixing all of my strings with N to cast them.
Am I missing something? Is there anything else that needs to be done on the criteria or the mapping? Additionally, the length of the parameters is not a constant 50 either. I am using NHibernate 2.1.
Try using the sql-type attribute in your mappings:
<property name="FirstName" length="50" />
<column sql-type="varchar(50)" />
</property>
What performance issues are you having? It seems unlikely to me that treating strings as unicode would affect performance all that much.
Try this
<property name="FirstName" length="50">
<column name="first_name" sql-type="varchar(50)" />
</property>
I have a class mapped with NHibernate and I'm trying to use a custom sql-query for loading. Special thing is that it uses a composite-id but I would expect that that's not a problem here.
Here's a simplified version of the mapping:
<class name="Person" mutable="false">
<composite-id>
<key-property name="PropertyA" column="propA" type="int" />
<key-property name="PropertyB" column="propB" type="string" />
</composite-id>
<property name="PropertyC" column="propC" type="datetime" />
<loader query-ref="loadPersons" />
</class>
<sql-query name="loadPersons">
<return class="PV" />
<![CDATA[
SELECT propA, propB, propC FROM MyPersons
]]>
</sql-query>
The problem is that the loader is completely ignored. The query sent to the database is completely generated as if the <loader> element would not be there:
SELECT this_.propA, this_.propB, this_.propC FROM Person this_
This obviously results in the error: table or view does not exist (because 'Person' is just our clean name).
Anyone knows if this is related to the composite-id or is there another reason why loader would be ignored?
Please note that there may be an error in returning the data as well. I've seen there's a special alias-syntax for that but I can't figure that out before the loader is actually doing something...
I know this is a very old question, but I was looking for the answer and came up with something. You use a query, wrapped in parentheses, as the table. It's a little ugly, but it works for me:
<class name="Person" mutable="false" table="(SELECT propA, propB, propC FROM MyPersons)">
<composite-id>
<key-property name="PropertyA" column="propA" type="int" />
<key-property name="PropertyB" column="propB" type="string" />
</composite-id>
<property name="PropertyC" column="propC" type="datetime" />
</class>
I have a inheritance hierarchy which I have mapped in NHibernate using Table-per-class. My mappping file looks like the one below (lots of properties omitted).
To query this hierarchy, I am building a dynamic DetachedCriteria for Message based on filter input from the user. Messages (of any type in the hierarchy) should be returned to the user in one list.
I would like to build a criteria based on the type of message, ie. the user could specify to get all messages of type SMSMessage or EmailMessage with a ReceivedDate > '2009-01-01'. How would I go about to do that?
In the same query, the user could specify that if the Message is an InternalMessage, it should have Priority = 2. How would I specify such type-specific predicates?
All this is possible to do in LINQ, so I am hoping I can do it in NHibernate as well.
<class name="Message" table="Message" abstract="true" discriminator-value="null">
<id name="MessageId">
<generator class="identity" />
</id>
<discriminator column="Type" type="byte" />
<property name="ParentId" />
<property name="ReceivedDate" />
...
<subclass name="SMSMessage" discriminator-value="0">
<property name="Text" column="Text" />
...
</subclass>
<subclass name="MMSMessage" discriminator-value="1">
<property name="Subject" />
...
</subclass>
<subclass name="EmailMessage" discriminator-value="2">
<property name="BodyPlainText" />
...
</subclass>
<subclass name="InternalMessage" discriminator-value="4">
<property name="Priority" />
...
</subclass>
</class>
I kind of figured this out myself, but in the end I ended up reverting to pure SQL since I hit too many roadblocks with HQL/Criterias. Anyways, I can share how I did this.
Maybe not pretty, but I solved it by adding the discriminator column as a regular property to the top level class in the hierarchy (Message) and employed restrictions against that column.
It turns out that you can specify restrictions against properties for subclasses even in the top-level query, so this was easier than I thought. It was just a matter of specifying the restrictions.
I have two classes: Family and Address.
A family has a physical address and a mailing address.
The mapping file for Family looks like:
....
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="native"></generator>
</id>
<many-to-one name="PhysicalAddress" class="Address" column="PhysicalAddressId" cascade="all" unique="true" />
<many-to-one name="MailingAddress" class="Address" column="MailingAddressId" cascade="all" unique="true" />
...
The mapping file for Address looks like:
...
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="native"></generator>
</id>
<property name="StreetAddress1" column="StreetAddress1" />
<property name="StreetAddress2" column="StreetAddress2"/>
<property name="City" column="City" />
<property name="State" column="State" />
<property name="ZipCode" column="ZipCode" />
...
(Note that Family-PhysicalAddress and Family-MailingAddress are one-to-one relationships.)
What I would like to happen is that when I execute
aFamily.MailingAddress = null;
session.Save(aFamily);
session.Flush();
I expect NHibernate to automatically delete the mailing address record from SQL Server for me.
BUT, that doesn't happen. NHibernate does not delete the address record from SQL Server.
Is there any way I can make it work?
Thank you!
This behaviour isn't supported by NHibernate. Of course the problem is that you probably don't have access to the NHibernate session in your domain logic where the change is made.
One possible -- though admittedly not ideal solution -- is to simply run another process to clean up orphaned entities.
Here is a discussion of this scenario:
http://colinjack.blogspot.com/2008/03/nhibernate-gotchas-orphans-and-one-to.html
And a link to a ticket on the issue:
https://nhibernate.jira.com/browse/NH-1262
Unfortunately NHibernate currently does not support automatic deletions of orhphans for many-to-one (Hibernate v3 in Java does support it). It is only supported on lists (cascade="all-delete-orphan").
What you can try to do is to use component mapping. Maybe it is possible to embed many-to-one into a component.
But I think it would better to explicitly delete the related object.