I have a master record which I'd like to use with. Here's a very basic example mapping example:
<class name="Master">
<join table="Detail">
<key>
<column name="Id" />
</key>
<property name="Name" />
</join>
</class>
This is all very well, and would work but for one problem. The Detail table looks like:
Master_ID EffectiveTo Name
1 1/1/2010 Colin
1 NULL ColinRamsay
There are multiple Detail records for each master, with the current one being the record with a NULL EffectiveTo. With the , I only want that current record to be joined and no other ones.
I'm not sure this is even possible with NH, I know I could create a many-to-one relationship here but I'd also have to create the Detail class and hbm.xml mapping. just seems simpler.
Any suggestions?
join is used to map properties of one class to several tables, when there's a 1-to-1 relationship between the tables.
You can use custom SQL for create, update and delete and load.
Another option is to join onto a view that selects the current records of the Detail table.
Related
Following on from NHibernate one-to-one vs 2 many-to-one
Is there an easy way to maintain multiple one-to-many relationships which are being used as a pseudo one-to-one.
E.g.
If I have 2 entities, User and Contact, which are related by a FK on each (User.ContactId and Contact.UserID).
What is the best way to maintain that each reference points at the other. It would be wrong for the system to update User with a different contact, but the Contact still references User...
Most likely you don't need to maintain this at all if you remove one of redundant foreign keys. Your database schema should not allow anomalies like that (userX references contactX but contactX references userY). Conceptually you have one-to-one relationship between user and contact. Why not have one-to-one in NHibernate mappings? If this is because of lazy loading that is not supported for nullable one-to-one in NHibernate? There is a solution to this problem that does not involve redundant foreign keys in the database.
1) In User mapping define a bogus list. List can have only one or zero items. Zero is treated as NULL (no Contact).
<bag
name="_contact"
table="UserContacts"
lazy="true"
inverse="true"
cascade="all-delete-orphan" >
<key column="UserId" />
<one-to-many class="Contact" />
</bag>
In Contact mapping define one-to-one:
<one-to-one name="_user" class="User" constrained="true" />
In the database you need to have PK Users.Id and one (!) foreign key Contacts.UserID.
2) Another option is to simply have many-to-one in User mapping and one FK Users.ContactId
<many-to-one
name="_contact"
column="ContactId"
cascade="all-delete-orphan"
unique="true"
lazy="proxy"/>
Either way the maintenance that you asked about is not needed and anomalies are not possible.
I have a ternary association table created using the following mapping:
<map name="Associations" table="FooToBar">
<key column="Foo_id"/>
<index-many-to-many class="Bar" column="Bar_id"/>
<element column="AssociationValue" />
</map>
I have 3 tables, Foo, Bar, and FooToBar.
When I delete a row from the Foo table, the associated row (or rows) in FooToBar is automatically deleted. This is good.
When I delete a row from the Bar table, the associated row (or rows) in FooToBar remain, with a stale reference to a Bar id that no longer exists. This is bad.
How can I modify my hbm.xml to remove stale FooToBar rows when deleting from the Bar table?
I haven't tested this... but you could get away by mapping FooToBar in Bar too, like this:
<map name="Associations" table="FooToBar">
<key column="Bar_id"/>
<index-many-to-many class="Foo" column="Foo_id"/>
<element column="AssociationValue" />
</map>
Keep in mind NH does NOT know that foo1[bar1] and bar1[foo1] represent the same row, so be careful with the in-memory state (that is, don't access the elements from both sides in the same session)
I have two entities, C and P.
C is mapped to P in a one-to-one association, with lazy="no-proxy",
like this: (P's mapping:)
<one-to-one name="c" class="C" property-ref="P" access="field" lazy="no-proxy"/>
P is mapped to C in a many-to-one association, like this: (C's mapping:)
<many-to-one name="p" column="PId" class="P" access="field" lazy="no-proxy" not-null="false"/>
usually I use lazy fetching, but in some cases I use FetchMode.Join to avoid the N+1 SELECTs problem, like this:
criteria.SetFetchMode("p", FetchMode.Join)
however, using FetchMode.Join for the C entity performs the query with a left outer join, and then immediately performs N more queries, fetching P's by ID! (profiling courtesy of NHProf)
any idea why this is happening?
answering #KLE's question, here's an excerpt from the documentation here
For a primary key association, add
the following mappings to Employee and
Person respectively:
<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>
Ensure that the primary keys of the related rows in the PERSON and EMPLOYEE tables are equal.
Alternatively, a foreign key with a
unique constraint, from Employee to
Person, can be expressed as:
<many-to-one name="person" class="Person" column="PERSON_ID" > unique="true"/>
This association can be made
bidirectional by adding the following
to the Person mapping:
<one-to-one name="employee" class="Employee" property-ref="person"/>
please let me know if I misunderstood this. thanks.
Don't know which version of NHibernate you are using but with 2.1.0 - 2.1.2 i've had some issues regarding FetchModes and complex mappings. I hadn't ran into property-ref (which obviously confuses the property walker) but i did have problems with some union classes.
Have you tried setting lazy="true" / "extra" and then define FetchMode.Join ?
I have a NewsFeed object mapped as such:
<class name="NewsFeed">
<id name="NewsFeedId">
<generator class="guid"/>
</id>
<property name="FeedName" not-null="true" />
<property name="FeedURL" not-null="true" />
<property name="FeedIsPublished" not-null="true" />
</class>
And Users who can have a Set of Selected feeds that they might be intereseted in, mapped like so:
<class name="SystemUser">
<id name="SystemUserId">
<generator class="guid"/>
</id>
<set name="SelectedNewsFeeds" table="SystemUserSelectedNewsFeeds" cascade="all">
<key column="SystemUserId" />
<many-to-many column="NewsFeedId" class="NewsFeeds.NewsFeed, Domain"/>
</set>
</class>
What I want to happen is when I delete the parent NewsFeed then all of the SelectedNewsFeed references get deleted too, without having to load each SystemUser and delete the NewsFeed by hand.
What is the best way to achieve this?
UPDATE: Using cascade="all-delete-orphan" instead of "all" still results in an exception when deleting the NewsFeed:
The DELETE statement conflicted with the REFERENCE constraint "FKC8B9DF81601F04F4". The conflict occurred in database "System", table "dbo.SystemUserSelectedNewsFeeds", column 'NewsFeedId'.
JMCD
Your second approach:
Another alternative is to break the
many-to-many relationship with a join
class in the middle which nHiberate
would be able to determine
parent-child relationships and the
cascade should work.
is actually what the nHibernate folks recommend in their documentation.
Don't use exotic association mappings.
Good usecases for a real many-to-many
associations are rare. Most of the
time you need additional information
stored in the "link table". In this
case, it is much better to use two
one-to-many associations to an
intermediate link class. In fact, we
think that most associations are
one-to-many and many-to-one, you
should be careful when using any other
association style and ask yourself if
it is really neccessary.
Using two one-to-many associations adds the flexibility to easily add other attributes to the "subscription", such as notification preferences for that particular subscription.
Since the relation inside the set is many-to-many, nHibernate is not able to tell which end of the relationship is the child and which is the parent, and the quickest way for me to achieve what I wanted was just to write some SQL that I sent through my repository that deleted the respective news feeds from the collection, and then deleted the parent news feed. The next time the collection was hydrated the changes were reflected.
Another alternative is to break the many-to-many relationship with a join class in the middle which nHiberate would be able to determine parent-child relationships and the cascade should work.
change
cascade="all"
to
cascade="all-delete-orphan"
Reference
I have an existing database with the table Transactions in it. I have added a new table called TransactionSequence where each transaction will ultimately have only one record. We are using the sequence table to count transactions for a given account. I have mapped this as a one-to-one mapping where TransactionSequence has a primary key of TransactionId.
The constraint is that there is an instead of trigger on the transaction table does not allow updates of cancelled or posted transactions.
So, when the sequence is calculated and the transaction is saved, NHibernate tries to send an update on the transaction like 'UPDATE Transaction SET TransactionId = ? WHERE TransactionId = ?'. But this fails because of the trigger. How can I configure my mapping so that NHibernate will not try to update the Transaction table when a new TransactionSequence table is inserted?
Transaction mapping:
<class name="Transaction" table="Transaction" dynamic-update="true" select-before-update="true">
<id name="Id" column="ID">
<generator class="native" />
</id>
<property name="TransactionTypeId" access="field.camelcase-underscore" />
<property name="TransactionStatusId" column="DebitDebitStatus" access="field.camelcase-underscore" />
<one-to-one name="Sequence" class="TransactionSequence" fetch="join"
lazy="false" constrained="false">
</one-to-one>
</class>
And the sequence mapping:
<class name="TransactionSequence" table="TransactionSequence" dynamic-update="true">
<id name="TransactionId" column="TransactionID" type="Int32">
<generator class="foreign">
<param name="property">Transaction</param>
</generator>
</id>
<version name="Version" column="Version" unsaved-value="-1" access="field.camelcase-underscore" />
<property name="SequenceNumber" not-null="true" />
<one-to-one name="Transaction"
class="Transaction"
constrained="true"
foreign-key="fk_Transaction_Sequence" />
</class>
Any help would be greatly appreciated...
One to one mapping in nhibernate doesn't work the way you think it does. It's designed so that you have two classes, which when persisted to their corresponding tables have the same primary keys.
However you can make it work, but it's not pretty. I'll show you how then offer up some alternatives:
In your Transaction hbml:
<one-to-one name="Sequence" class="TransactionSequence" property-ref="Transaction"/>
In your Sequence html:
<many-to-one name="Transaction" class="Transaction" column="fk_Transaction_Sequence" />
This should do what you want it to do. Note the property-ref.
The next question you're going to post on is going to ask how you get lazy loading on one-to-one associations. The answer is, you can't... well you can, but it probably won't work. The problem is that you have your foreign key on the sequence table, which means that nhibernate has to hit the database to see if the target exists. Then you can try playing around with constrained="true/false" to see if you can persuade it to lazily load the one-to-one association.
All in all, it's going to result in a total waste of your time.
I suggest either:
Have two many-to-one associations.
Have a many-to-one association with a collection on the other end.
This will save you a lot of headaches in the long run.
Turns out that for my situation a <join table> mapping worked best. I just had to make sure that I made the properties that came from the second table were nullable types, or it would do an insert on save even if nothing had changed. Since I did not need lazy loading for the second table, this works great. I am sure that I could have gotten paired many-to-one mappings to work, but it was not intuitive and seems more complicated than the join table option, however <join table> is only available in NHibernate 2.0 and up.