-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 
Author Message
 Post subject: Understanding Relationships
PostPosted: Thu Mar 25, 2010 10:22 am 
Newbie

Joined: Tue Mar 23, 2010 10:41 pm
Posts: 9
I am new to using Nhibernate and try to understand how i work with relationships when saving an object.

I have read a couple of places i need to set both side of a relationship, but i am not sure i understand it correctly as i think it will cause a lot of unnecessary selects if i always have to set it on both sides.

Will I only have to set both sides of a relationship if i am setting it through the Inverse side ?
In other words should the Primary side (the side that contains the foreign key) always be set ? and the other side only matters if i want to set i through that side ?

Heres is a very simple example

I have the following database tables

Users
-----
- Id (Primary Key)
- CountryId (Foreign key)

Countries
---------
- Id (Primary Key)
- Name

I have the following entities / models which maps to the tables

User
----
- Id (int)
- Country (Country)

Country
-------
- Id (int)
- Name (String)
- Users (IList<User>)


A User can have 1 country, and a Country can have many Users.
User is the Primary side (containing the CountryId foreign key), and Country is the inverse side of the relationship.

1) If I set Country on the User object i dont need to set it on the other side (- do not have to add it to the "IList<User> Users" collection on Country) ?

2) If I want to add the User to the Country (through the Country object) I need to add User to the "IList<User> Users" collection on the Country and set Country on the User ?


What i am wondering is how i can add a Country to a User without having to select both entities first ?

I like it to just fire something like "UPDATE User SET CountryId = @CountryId WHERE Id = @UserId"

If i always need to set up the relationship on both sides it will require a select of both the User and the Country (which i am trying to prevent), or at least only select the User and set the Country by doing something like "user.Country = session.Load<Country>(123); session.Save(user);"


Hope someone out there can help me understand how it works.


Best regards

Martin


Last edited by MartinFrom on Sat Mar 27, 2010 9:48 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Fri Mar 26, 2010 6:14 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Quote:
I have read a couple of places i need to set both side of a relationship


Yes, on bidirectional relations according documentation you should always set both sides of a relationship.
If you set only the 'primary' side, the update on the database might be made correctly, but you could run into other
problems which are not visible immediately. Always set both sides if you want walk on the save side of the way.

Quote:
i think it will cause a lot of unnecessary selects if i always have to set it on both sides.


From my experience, setting the association on both sides don't causes further selects.

Quote:
What i am wondering is how i can add a Country to a User without having to select both entities first ?


That's possible with bulk updates.


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Fri Mar 26, 2010 4:47 pm 
Newbie

Joined: Tue Mar 23, 2010 10:41 pm
Posts: 9
Thanks for your reply.

What i mean about selects if i have to set relationship on both sides is that I will have to Select both sides of the relationship.

It just feels wrong to do 2 selects in order to do a simple "UPDATE Users SET CountryId = @CountryId WHERE (Id = @UserId)"


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Sat Mar 27, 2010 3:45 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Please explain what you understand under 'select'.
Do you mean 'select' the object through load (session.get(...) or session.load(...) , em.getReference(...) , em.find(...))
or do you mean 'select' the object through a query (session.createQuery() or em.createQuery()) ?


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Sat Mar 27, 2010 9:45 am 
Newbie

Joined: Tue Mar 23, 2010 10:41 pm
Posts: 9
Thanks again for your reply.

What i mean is that i will some how have to query both sides of the relationship (which requires two SQL select statements).

Etc in my example if i want to add a Country to my User, and i have both the User Id, and the Country Id, i will have to first query the User (any way i do it will require a SQL Select statement to be fired ? easiest way is using Get<>(Id) i guess ?), and then query the Country (Get<>(Id)) to be able to add the Country object to the User object.

As far as i know both - .Get(), .Load(), .CreateQuery() will make a SQL Select from the Database to get the data needed for creating the Object ?

Load() might not do it in the first place and only return some kind of proxy ? but when the proxy is accessed to set the relationship (country.Users.Add(theUser);) it will query the object by doing a SQL select anyway ?


ps. i am new to Nhibernate soo i might be misunderstanding something :)


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Mon Mar 29, 2010 3:00 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Quote:
Load() might not do it in the first place and only return some kind of proxy ? but when the proxy is accessed to set the relationship (country.Users.Add(theUser);) it will query the object by doing a SQL select anyway ?


No.
I just tried following code

Code:
a = em.find(A.class, id_a);
b= em.getReference(B.class, id_b); // creates a proxy on b, corresponds to session.load(...)
a.assSingleB = b;  // sets the association without 'select' b
commit


and as expected the association is set without doing a query on b.

Code:
select
        a0_.oid as oid4_1_,
        a0_.assSingleD_oid as assSingleD4_4_1_,
        a0_.name as name4_1_,
        a0_.version as version4_1_,
        b2_.oid as oid6_0_,
        b2_.assA_oid as assA3_6_0_,
        b2_.version as version6_0_
    from
        A a0_
    left outer join
        B b2_
            on a0_.assSingleB_oid=b2_.oid
    where
        a0_.oid=1
p6spy - 1269845791745|-1||resultset|A_oid=null, assSingleB_oid=null, name=null, oid=null, version=0
p6spy - 1269845791755|0|0|statement
   
    update
        A
    set
        assSingleB_oid=1,
        version=1
    where
        oid=1
        and version=0
p6spy - 1269845791758|0|0|commit||



N.B.: I use Hiberante but I assume it is same behavior in NHibernate.


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Mon Mar 29, 2010 3:09 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Conclusion: When setting a relation between two persistent objects then at least the owner side of the relation must be loaded (= 'selected' using your term) in persistent context. The non-owner side of the relation can be a non-materialized proxy,
in this way the 'select' on the 'target' object can be avoided.

If you don't want to load any side of the relation for setting the relation,
then you can use bulk-updates.

By the way: why you feel so bothering about the fact that a 'select' is issued before the update?
After all usually a select with condition on primary key just takes some milliseconds to execute.


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Mon Mar 29, 2010 5:59 pm 
Newbie

Joined: Tue Mar 23, 2010 10:41 pm
Posts: 9
Thanks again for your reply.

In your code example you only set up one side of the relationship - which i asked about earlier and was told that I need to set up both sides.


This will make it fire a unnessecary SQL Select when the AddUser() method on country is called - setting up both sides in a relationship

Code:
User user = session.Get<User>(111);
Country country = session.Load<Country>(123);
user.Country = country; // Set up primary side
country.AddUser(user); // Set up inverse side - will make it fire a SQL Select immediatly when the method is called on the proxy
session.Save(user);


If i only set the primary side like in your code example (the side which contains the foreign key - in my example the User object) then it works fine and no extra SQL Select is fired. But then i dont set both side of the realtionship.

This works like in your example - but only sets up one side of the relationship

Code:
User user = session.Get<User>(111);
user.Country = session.Load<Country>(123); // Set up primary side
session.Save(user);


Is it only necessary to set up both side of the relationship if both objects are queryed (materialized) ? or when is it needed (i am even more confused now as it was what i tried to ask in the first post) :) ?

In case it is only needed to set up both side of the relationship when the object being added is already queryed / materialized, then how can i check if the object is already queryed (without it being queryed if it isnt) and set up the relationship ?



The reason i am concerned about the "Selects" is because I am going to have some tables with many columns (some with large texts), and i dont want to transfer the data every time when it is not needed.
Also, i am new to NHibernate soo i am pretty sure i must be doing something wrong, when i cant find out to make something this simple work :) , and I will like to learn how to use Nhibernate correctly.


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Mon Mar 29, 2010 10:05 pm 
Newbie

Joined: Tue Mar 23, 2010 10:41 pm
Posts: 9
I have created this small helper that i can use to test if an Object is an Uninitialized Proxy (or more important not a proxy or a proxy that have been initialized)

Code:
public static Boolean IsUninitializedProxy(this Object obj)
{
   INHibernateProxy proxy = obj as INHibernateProxy;
   if (proxy != null)
   {
      return proxy.HibernateLazyInitializer.IsUninitialized;
   }
   return false;
}


Then i can do something like

Code:
User user = session.Get<User>(111);
Country country = session.Load<Country>(123);
user.Country = country; // Set up primary side
if (!country.IsUninitializedProxy())
{
   country.AddUser(user); // Set up inverse side - either not a proxy or it have been initialized
}
session.Save(user);


Then it works perfectly it seems.

But is this the correct way of doing it ? (only setting up both sides of a relationship if the inverse side is not a proxy or initialized) ?


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Wed Mar 31, 2010 8:58 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Quote:
In your code example you only set up one side of the relationship -


Sorry, yes, my example was build on a unidirectional relation.

Quote:
only setting up both sides of a relationship if the inverse side is not a proxy or initialized ... Then it works perfectly it seems. But is this the correct way of doing it ?


According hibernate documentation you always should set both sides of relations if it is bidirectional,
In practice I see that it is enough to set the owner side (at least on OneToMany relations) in order that the information is properly saved on database when data gets flushed.
But there are some side-effects to be aware, when not setting the inverse-side (=the collection) too.
Following are the side-effects which I know:
- the inverse-side collection it not up-to-date anymore (as you don't add the new item) in transaction
- the inverse-side collection continues to remain not up-to-date over the original transaction
if you detach (or reattach) the owning entity.
- the inverse-side collection is again up-to-date when you re-read the entity from database (not using reattachement)
- if the inverse-side collection is configured to be cached in 2L-cache then it would be completely rebuilded and replaced in cache instead do be simply updated.


Top
 Profile  
 
 Post subject: Re: Understanding Relationships
PostPosted: Thu Apr 08, 2010 3:07 pm 
Newbie

Joined: Tue Mar 23, 2010 10:41 pm
Posts: 9
Thanks a lot for your time and help.

Being used to write the Sql myself I just feel like a bad developer doing unessecary selects just to be able to updates and inserts.
But basically it doesnt matter as they are only needed when updating / inserting something which happens rarely, and i can make some relationships uni-directional.
Ill better focus on something else which is more important. I just wanted to know I was doing everything the right way.

Thanks again :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.