In my database I've got Users and UserGroups which have a many-to-many relation. The User has a set of UserGroup, the UserGroup domain object does not see the User.
<class name="User" table="UserTable">
<set name="UserGroup" cascade="save-update" access="field.pascalcase-underscore" table="User2UserGroup">
<key column="User_Id" />
<many-to-many class="UserGroup" column="UserGroup_Id" />
</set>
...
What I'm trying to achieve is the nhibernate deleting the correlation from the junction table when I delete either a User or a User Group. Additionally User and Group are child objects of, let's call it a Domain. Domain does cascade="all-delete-orphans", so when a Domain gets deleted it cascade-deletes all its Users and UserGroups.
Back to the User<->UserGroup relation: If I understand correctly I can't use any form of cascade that involves delete as I just want to delete the association between two objects and not the related object itself. (A group shall not vanish, even if it's an orphan. And a User without a group is a valid thing in my world - he just has no rights to do anything at all.)
Do I need to look at events/interceptors? Or can I do what I want to achieve by controlling the mapping?
If you are using a database that supports it can you not set the cascade on the database itself in the form of a Foreign Key constraint?
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 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.
I have some database tables named Project, Employee and Branch. An employee can work simultaneously on more than one project. Similarly, in a project, there are multiple employees. Also, a project is conducted at a particular branch. To maintain all these relationships, I am using a project_employee_branch table, which will store the related primary keys of the above three tables. As an example, this project_employee_branch table may contain a row like (1,2,3), which means the project whose primary key is 1, is conducted at branch whose primary key is 3, and one of its project member is an employee whose primary key is 2.
How can I map all these associations in NHibernate? I have mapped many-to-one association using foreign key concept, but I don't know how to map these types of associations, where an intermediate table is involved.
First point I'd make is that your database schema and your description don't match, so please take any advice below in the light of that initial caveat. You say that
a project is conducted at a particular branch
which implies there should be a simple foreign key relationship from project to branch. And of course, if this is what the schema looked like, you would have a two-way many-to-many link table and your life would be much easier.
Anyway, with the three-way combination you have, you need to have a collection of components, where the components have many-to-one properties for the other two object types. There is an example in section 7.2 of the NHibernate documentation, but I think it would look something like this in the mapping for Product:
<set name="BranchEmployees" table="product_employee_branch" lazy="true">
<key column="product_id">
<composite-element class="Purchase">
<many-to-one name="Branch" class="Branch" />
<many-to-one name="Employee" class="Employee"/>
</composite-element>
</set>
I have been trying to get to grips with Hibernate's inverse attribute, and it seems to be just one of those things that is conceptually difficult.
The gist that I get is that when you have a parent entity (e.g. Parent) that has a collection of Child objects using a one-to-many mapping, setting inverse=true on the mapping tells Hibernate that 'the other side (the Child) has responsibility to update itself to maintain the foreign key reference in its table'.
Doing this appears to have 2 benefits when it comes to adding Children to the collection in your code, and then saving the Parent (with cascade-all set): you save an unneccessary hit on the database (because without inverse set, Hibernate thinks it has two places to update the FK relationship), and according to the official docs:
If the column of a
association is declared
NOT NULL, NHibernate may cause
constraint violations when it creates
or updates the association. To prevent
this problem, you must use a
bidirectional association with the
many valued end (the set or bag)
marked as inverse="true".
This all seems to make sense so far. What I don't get is this: when would you NOT want to use inverse=true on a one-to-many relationship?
As Matthieu says, the only case where you wouldn't want to set inverse = true is where it does not make sense for the child to be responsible for updating itself, such as in the case where the child has no knowledge of its parent.
Lets try a real world, and not at all contrived example:
<class name="SpyMaster" table="SpyMaster" lazy="true">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name"/>
<set name="Spies" table="Spy" cascade="save-update">
<key column="SpyMasterId"/>
<one-to-many class="Spy"/>
</set>
</class>
<class name="Spy" table="Spy" lazy="true">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name"/>
</class>
Spymasters can have spies, but spies never know who their spymaster is, because we have not included the many-to-one relationship in the spy class. Also (conveniently) a spy may turn rogue and so does not need to be associated with a spymaster. We can create entities as follows:
var sm = new SpyMaster
{
Name = "Head of Operation Treadstone"
};
sm.Spies.Add(new Spy
{
Name = "Bourne",
//SpyMaster = sm // Can't do this
});
session.Save(sm);
In such a case you would set the FK column to be nullable because the act of saving sm would insert into the SpyMaster table and the Spy table, and only after that would it then update the Spy table to set the FK. In this case, if we were to set inverse = true, the FK would never get updated.
Despite of the high-voted accepted answer, I have another answer to that.
Consider a class diagram with these relations:
Parent => list of Items
Item => Parent
Nobody ever said, that the Item => Parent relation is redundant to the Parent => Items relation. An Item could reference any Parent.
But in your application, you know that the relations are redundant. You know that the relations don't need to be stored separately in the database. So you decide to store it in a single foreign key, pointing from the Item to the Parent. This minimal information is enough to build up the list and the reference back.
All you need to do to map this with NH is:
use the same foreign key for both relations
tell NH that one (the list) is redundant to the other and could be ignored when storing the object. (That is what NH actually does with inverse="true")
These are the thoughts which are relevant for inverse. Nothing else. It is not a choice, there is only one way of correct mapping.
The Spy Problem:
It is a completely different discussion if you want to support a reference from the Item to the Parent. This is up to your business model, NH doesn't take any decisions in this. If one of the relations is missing, there is of course no redundancy and no use of inverse.
Misuse: If you use inverse="true" on a list which doesn't have any redundancy in memory, it just doesn't get stored. If you don't specify the inverse="true" if it should be there, NH may store the redundant information twice.
If you want to have an unidirectional association i.e. that the children can't navigate to the Parent. If so, you FK column should be NULLABLE because the children will be saved before the parent.
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