Lazy Loaded One-To-One With NHibernate
UPDATE 20081114
The one-to-one solution I
had posted turned out not to work for updates, :(, in the
end I had to use a many-to-one map with a unique foreign key
association to get this to work, the updated example is
below. Sorry for my EPIC FAIL :)...
G'day,
I am working on something at the moment and I am storing
BLOB data for documents in the database, I am storing things
like name and data which is a binary field. |
Now I
wan't to load the binary data as loading this all the time
is very inneficient. So to get this to work I had to split
this to two tables, one with my meta data and another
one-to-one
table to store the BLOB. NOW the fun began I
created the two tables:
Documents
-------------------
(PK) Id
(FK
Unique) DocumentFileId
Name
DocumentFiles
-------------------
(PK)
Id
Data
From this I created my POCO classes for the Document
and DocumentFile map.
public class Document {
public int Id { get; set;
}
public string Name { get; set; }
public
DocumentFile DocumentFile { get; set; }
}
public class DocumentFile {
public virtual int Id
{ get; set; }
public virtual Document Document {
get; set; }
public virtual byte[] Data { get; set;
}
}
Now on to the mappings, I thought this would be as
easy as created a one-to-one mapping with lazy="proxy" set
on the one-to-one on the Document class but this was not the
case.
You NEED to set constrained="true" on the
mapping, basically going from this post I found:
http://www.hibernate.org/162.html#A5. Say we have A->B where this is a 1-1 relationship,
now
without a constraint from A-B this means A can exist without
B, so there is a possiblity that B is null, a Proxy to B
will be not null and won't work here.
But when we
know A and B will always belong together it is ok to create
a Proxy for B. Now my final mapping files looks like so:
BUUUT as I soon found out if in the A mapping you
constrain B it will mean it will try to insert B first and
fail to generate a primary key with the foreign key
generator, hence not working as expected, I had to change
the mappings to use a many-to-one unique foreign key mapping
to get this to work. I updated the mappings below, the main
differences are highlighted below:
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2">
<class
name="NHibernateDocumentTest.Document,
NHibernateDocumentTest" table="Documents"
lazy="false">
<id name="Id" column="Id"
type="integer">
<generator class="native"
/>
</id>
<property name="Name"
column="Name" type="string" />
<many-to-one name="DocumentFile"
cascade="all-delete-orphan"
lazy="proxy" column="DocumentFileId" unique="true"
/>
</class>
</hibernate-mapping>
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2">
<class
name="NHibernateDocumentTest.DocumentFile,
NHibernateDocumentTest" table="DocumentFiles"
lazy="true">
<id name="Id" column="Id" type="integer">
<generator class="native" />
</id>
<property name="Data" column="Data"
type="Byte[]" />
<one-to-one name="Document" constrained="true"
property-ref="DocumentFile" />
</class>
</hibernate-mapping>
And then we are done, this works fine. So the
trick is for a one-to-one to work with Lazy loading you must
use a unique foreign key associated with a many-to-one
mapping to your child.
Thanks
Stefan