NHibernate Subclass of a Joined-Subclass problem - nhibernate

I have an application that has a core assembly with base classes that I need to inherit from.. I need to save these to the database and after reading about NHibernate decided to use it.
However I have a problem with one of my new inherited classes.. I have setup the subclass map but when I save, it neither attempts to save any of it's base class properties or any of it's new ones that I have assigned in the mapping!
My classes are laid out like the following: (from a small demo app)
core assemblies
DataItem -> User
Anything that will touch the database inherits the DataItem class as it handles the id, modified date etc etc..
In my test I setup user to only have a FirstName..
If I save a new User it works great.. however when I inherit from user and then add another property called LastName and attempt to save this new object.. it only puts a sql statement together of INSERT INTO t_User (id) VALUES(?).. it doesn't attempt to save the first name or last name.. either though both have been set and are mapped.
My nhibernate.config:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory name="DAL">
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="adonet.batch_size">16</property>
<property name="current_session_context_class">web</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<mapping assembly="DAL"/>
<mapping assembly="NHibernateDemo"/>
</session-factory>
</hibernate-configuration>
As you can see I have 2 assemblies.. my DAL is my core and the NHibernateDemo is a web application that uses the core for inheritance.
My core DataItem mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DAL" namespace="DAL.Model">
<class name="DataItem" table="t_DataItem" >
<id name="Id">
<generator class="native" />
</id>
<discriminator column="typeid" type="System.Int32"></discriminator>
<property name="IsActive" column="isActive" not-null="true" />
<property name="TypeId" column="typeId" not-null="true"></property>
<many-to-one name="Parent" column="ParentId" class="DataItem"></many-to-one>
<bag name="Children" cascade="all-delete-orphan">
<key column="ParentId"></key>
<one-to-many class="DataItem"/>
</bag>
<joined-subclass name="User" table="t_Users">
<key column="id"></key>
<property name="FirstName" column="firstName" not-null="true" ></property>
</joined-subclass>
<joined-subclass name="Email" table="t_Emails">
<key column="emailid"></key>
<property name="Address" column="Address"></property>
</joined-subclass>
</class>
</hibernate-mapping>
My inherited NewUser mapping that doesn't work!:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateDemo" namespace="NHibernateDemo.Model">
<subclass name="NewUser" extends="DAL.Model.User, DAL" discriminator-value="1">
<property name="LastName" column="LastName"></property>
</subclass>
Why is it that when I attempt to save my class NewUser that it doesn't attempt to save any of the other properties set, whether from it's base or newly declared properties?
I'd really appreciate any help or insight to this.. I must be missing something really simple and I just can't see it.
Thanks,
Mike

It could be that you're not able to mix subclass and joined-subclass mappings in the same class hierarchy. Since your DataItem and User are related by joined-subclass, you may need to make your NewUser class another joined-subclass of User.
Another issue might be the use of the "discriminator" in your current NewUser mapping. The discriminator should be an additional column in your User table that NHibernate uses to tell the difference between a User record and a NewUser record. I'm not sure if this works where the User base class might not specify its discriminator value, whereas the NewUser does. I'm not sure if you're specifying the discriminator-column anywhere, which might also be a problem.
I would suggest first trying to make NewUser a joined-subclass of User.

Related

Lazy load a self-referencing table

I'm trying to map a self-referencing table with NHibernate 3.2.0.4000. However, whenever I get an instance of DomainObject, it eagerly loads the subsequent versions. I'd rather not have to put an extra column my table, though that is an option.
Can I have NHiberante not eagerly load all of the subsequent versions without maintaining the relationship on both sides?
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping assembly="NHibernateHierarchyTest" namespace="NHibernateHierarchyTest" xmlns="urn:nhibernate-mapping-2.2">
<class name="DomainObject" table="DOMAIN_OBJECT" lazy="true" >
<id name="DomainObjectId" column="DOMAIN_OBJECT_ID">
<generator class="identity" />
</id>
<property name="Property">
<column name="PROPERTY" />
</property>
<many-to-one name="PreviousVersion" class="DomainObject" >
<column name="PREVIOUS_VERSION_DOMAIN_OBJECT_ID" />
</many-to-one>
<!--<many-to-one name="SubsequentVersion" class="DomainObject">
<column name="SUBSEQUENT_VERSION_DOMAIN_OBJECT_ID" />
</many-to-one>-->
<one-to-one name="SubsequentVersion" class="DomainObject" property-ref="PreviousVersion" />
</class>
</hibernate-mapping>
The one-to-one mapping will be always loaded eagarly with NHibernate. Not sure if this is a feature or bug, but that is how it works. If you need lazy load, use many-to-one or one-to-many. Not the best answer I know, but if you can add new column...

Nhibernate StatelessSession and Lazy Loading

I recently tried to develope a "SessionWrapper". The idea behind it was to be able to switch from a Stateful Session to a Stateless Session easily, with no modifications to the DAOs and the XML mapping files.
Mapping looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Domain" assembly=Domain">
<class name="BatchScheduling" table="dbo.batch_scheduling">
<id name="ID" column="batch_scheduling_id">
<generator class="native" />
</id>
<property name="Name" column="batch_scheduling_name" not-null="true" />
<property name="Description" column="batch_scheduling_description" not-null="true" />
<property name="Type" column="batch_scheduling_type" not-null="true" />
<property name="Cron" column="batch_scheduling_cron" not-null="true" />
<property name="DateModified" column="batch_scheduling_date_modified" not-null="true" />
<bag name="Parameters" cascade="all" table="dbo.batch_scheduling_parameters" lazy="true" inverse="true">
<key column="batch_scheduling_id"/>
<one-to-many class="BatchSchedulingParameter"/>
</bag>
</class>
</hibernate-mapping>
I knew that Stateless Sessions do not support lazy loading. I expected the session to eager fetch all the related objects/collections declared in the mapping.
However, when I try to access BatchScheduling.Parameters I get the following exception:
NHibernate.LazyInitializationException: Initializing[Domain.BatchScheduling#22]-failed to lazily initialize a collection of role: Domain.BatchScheduling.Parameters, no session or session was closed
Any idea?
I knew that Stateless Sessions do not support lazy loading. I expected the session to eager fetch all the related objects/collections declared in the mapping.
That could result in the entire database being loaded into memory, and will in probably 80-90% of cases result in way more queries than are necessary and basically render the stateless session useless.
One approach you can use is to eagerly fetch the relationships you need, using something like SetFetchMode

NHibernate with Second Level Cache Not Rehydrating Properties Marked insert="false" update="false"?

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)

NHibernate Mapping Problem - Can I Map Multiple Assemblies?

I have the following mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Project1.Accounts"
namespace="Project1.Core.Domain">
<class name="Equipment" table="Equipment">
<id name="ID" column="ID">
<generator class="identity"></generator>
</id>
<property name="Name" />
<property name="Description" />
<property name="AccountID" />
<property name="EquipmentTypeID" />
<many-to-one name="Account" class="Project2.Core.Domain.Account, Project2.Core" column="AccountID"/>
<many-to-one name="EquipmentType" class="Insight.IT.Accounts.Core.Domain.EquipmentType, Insight.IT.Accounts" column="EquipmentTypeID"/>
</class>
</hibernate-mapping>
I'm getting the following error:
NHibernate.MappingException: An association from the table Equipment refers to an unmapped class: Project2.Domain.Account
Just to be clear - The Account class lives in a different assembly than the Equipment class does. The project that the Account class resides in has it's own hibernate.cfg.xml.
Basically, it looks like i need a way to reference multiple mapping assemblies in the hibernate.cfg.xml file. Is this possible??
You can do it like this:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">...</property>
<property name="connection.driver_class">...</property>
...
<mapping assembly="MyProject.OtherAssembly"/>
</session-factory>
</hibernate-configuration>
It is possible, you'll just have to indicate this in your mapping file (and it looks like you did this).
Did you add both the assemblies to the NHibernate configuration, before creating the sessionfactory ?
Thanks for the responses. Actually, I ended up doing it a little different. I just removed the tag from the hibernate.cfg and used fully qualified names in the .hbm.xml files. Thanks!!

NHibernate Joined Subclass in Separate Assemblies

I have the following solution project structure:
Application.Core.Entities
Application.Xtend.CustomerName.Entities
In the Core project I have an entity Customer defiend. In the XTend project, I have an entity defined that subclasses Customer named xCustomer (for lack of a better name at this time...).
The idea here is that we have a Core domain model in our application. A customer can then create a new assembly that contains extensions to our core model. When the extension assembly is present a smart IRepository class will return a subclass of the core class instead.
I am attempting to map this relationship in NHibernate. Using Fluent NHibernate I was able to generate this mapping:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
default-lazy="false"
assembly="NHibernate.Core.Entites"
namespace="NHibernate.Entites"
default-access="field.camelcase-underscore">
<!-- Customer is located in assembly Application.Core.Entities -->
<class name="Customer" table="Customers" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="Id" type="Int64">
<generator class="native" />
</id>
<component name="Name" insert="true" update="true">
<property name="LastName" column="LastName" length="255" type="String" not-null="true">
<column name="LastName" />
</property>
<property name="FirstName" column="FirstName" length="255" type="String" not-null="true">
<column name="FirstName" />
</property>
</component>
<!-- xCustomer is located in assembly Application.XTend.CustomerName.Entities -->
<joined-subclass name="xCustomer" table="xCustomer">
<key column="CustomerId" />
<property name="CustomerType" column="CustomerType" length="255" type="String" not-null="true">
<column name="CustomerType" />
</property>
</joined-subclass>
</class>
</hibernate-mapping>
But NHib throws the following error:
NHibernate.MappingException:
persistent class
Application.Entites.xCustomer,
Application.Core.Entites not found
---> System.TypeLoadException: Could not load type
'Application.Entites.xCustomer' from
assembly 'Application.Core.Entites,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'..
Which makes sense xCustomer is not defined in the Core library.
Is it possible to span different assemblies like this? Am I approaching the problem wrong?
I asked this same question on the NHibernate Users mailing list and the solution was so obvious that I am somewhat embarrassed that I couldn't see it.
The hibernate-mapping attributes assembly and namespace are convenient short cuts that allow you to not have to fully qualify your class names. This lets you have the nice mark up , but the name attribute of both class and joined-subclass elements can take a fully qualified assembly name as well.
So the above broken mapping file can be fixed like so:
<joined-subclass name="Application.XTend.CustomerName.Entities.xCustomer,
Application.XTend.CustomerName.Entities, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null"
table="xCustomer">
<key column="CustomerId" />
<property name="CustomerType" column="CustomerType" length="255"
type="String" not-null="true">
<column name="CustomerType" />
</property>
</joined-subclass>
This works as I expected it to. So I then took a look at the Fluent-NHibernate source and created a patch complete with working unit tests to resolve the issue and submitted it to the project.
Thanks for you help #David Kemp
You need to map using the extends attribute of the <class> element (AFAIK, this is new in NHibernate 2.0). Then you can have your subclass mapping (.hbm.xml) in the XTend assembly.
You might have to use the AddAttribute/AddProperty (can't remember what it's called) to do this using Fluent NHibernate. (Or submit a patch).