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
Related
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...
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 problem with my mappings Employee and Project: For example in Employee mapping I have "bag" to map other tables. The problem is when I open the Employee window, this take a lot of time(10 sec) opening the Window, how can I make the mapping better and also faster? Maybe in the lazy or in the fetch?.
This is the mapping for Employee:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="AdminProject"
namespace="AdminProject.Business.Entity">
<class name="Employee">
<id name="EmployeeId" type="int">
</id>
<property name="OperatorNum"
generated="always"
update="false"
insert="false"
type="int"/>
<property name="Password" type="string"/>
<property name="Name" type="string"/>
<property name="LastName" type="string"/>
<property name="DateBegin" type="DateTime"/>
<property name="DateEnd" type="DateTime"/>
<property name="Telephone" type="string"/>
<property name="Address" type="string"/>
<many-to-one
name="EmployeeState"
column="EmployeeStateId"
class="EmployeeState"
fetch="join"/>
<bag name="EmployeebyProject" lazy="false">
<key column="EmployeeId"/>
<one-to-many class="EmployeebyProject"/>
</bag>
<bag name="EmployeeComments" lazy="false">
<key column="EmployeeId"/>
<one-to-many class="EmployeeComments"/>
</bag>
</class>
</hibernate-mapping>
Thanks..
You should use SQL Server Profiler to figure out what is being loaded from your database. Alternatively you can set NHibernate to log SQL. This way it will be easier for you to see what causes the delay. Very likely that it is caused by eagerly loading collections (your mapping has lazy="false"). If this is the case you can simply set it to true (default).
<bag name="EmployeebyProject" lazy="true">
<key column="EmployeeId"/>
<one-to-many class="EmployeebyProject"/>
</bag>
<bag name="EmployeeComments" lazy="true">
<key column="EmployeeId"/>
<one-to-many class="EmployeeComments"/>
</bag>
Your many-to-one association is also loaded eagerly (fetch="join").
One of the popular approaches is to have all associations lazy (Lazy Default Fetch Plan). And then use eager loading in places where you are sure you will need association loaded. This however depends on your session management because lazy loading will not work if session is no longer available. There is a very good description of fetch plans and strategies in this book.
For your bag mapping, remove lazy="false". It's lazy by default.
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.
I have a true one to one mapping. But I would like to use lazy loading(load on demand).
I have Class Person with a association with Class Address. The mapping looks like this..
PERSON
<one-to-one name="address" class="Person" cascade="all-delete-orphan" access="field">
ADDRESS
<class name="Address" table="Address" lazy="true">
<id name="id" column="addressId" type="Int32" access="field">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" class="Address" constrained="true" access="field" />
Does anyone see something wrong with this? How do I enable proxy/lazy loading for address?
Thanks
Heres a good discussion on the topic http://ayende.com/Blog/archive/2007/05/10/NHibernate-onetoone.aspx
Crucial quote: "In other words, one-to-one cannot be lazily loaded, which is one of the reasons why it is recommended to use two many-to-one instead."
Also see https://www.hibernate.org/162.html and NHibernate: how to enable lazy loading on one-to-one mapping