-->
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.  [ 19 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Parent child problem when using SysCacheProvider
PostPosted: Tue Jun 13, 2006 1:16 am 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi,

I am having a problem with a parent/child relationship whereby the child is not getting persisted by NH and no exception is being thrown.

The parent child relationship is mapped as follows:

Code:
<bag name="Children" table="Child" inverse="true" lazy="true" cascade="all-delete-orphan" order-by="Description">
  <cache usage="read-write"/>
  <key column="ParentID"/>
  <one-to-many class="MyDomain.Parent, MyDomain"/>
</bag>


The Child also has caching enabled in its mapping file:

Code:
<class name="MyDomain.Child, MyDomain" table="Child">
  <cache usage="read-write"/>
  <property name="ID" type="Int32" not-null="true">
    <generator class="identity"/>
  </property>
  <property name="Description" type="String" length="200" not-null="true"/>
...


The Parent contains the following code to add a new child to the collection:

Code:
public void AddChild(Child child)
{
    child.Parent = this;
    this.Children.Add(child);
}



The new child persists fine if I remove the cache element on the bag. It also persists fine on all attempts other than the first attempt within a debugging session.

The problem seems like it might be related to lazy loading because if I add breakpoints in my code and inspect the Count or the contents of the bag before the child is added, then the new child gets persisted correctly.

Not sure if this is a bug or something that I am doing wrong.

Any help appreciated.

Thanks,

Jason


Top
 Profile  
 
 Post subject: Re: Parent child problem when using SysCacheProvider
PostPosted: Tue Jun 13, 2006 1:40 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
jason.hill wrote:
Hi,
The Parent contains the following code to add a new child to the collection:

Code:
public void AddChild(Child child)
{
    child.Parent = this;
    this.Children.Add(child);
}


[..]

The problem seems like it might be related to lazy loading because if I add breakpoints in my code and inspect the Count or the contents of the bag before the child is added, then the new child gets persisted correctly.


In case of lazy loading, ALL public methods and properties of the class must be virtual. The latest NH 1.2 alpha also checks whether they are..

Gert

_________________
If a reply helps You, rate it!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 1:48 am 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi Gert,

All my properties were marked virtual but I did not realise that all the methods needed to be as well.

I added the virtual modifier to all of the public methods but the problem still remains. Any other ideas?

Cheers,

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 2:48 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
jason.hill wrote:
All my properties were marked virtual but I did not realise that all the methods needed to be as well.

I added the virtual modifier to all of the public methods but the problem still remains. Any other ideas?


No, not really... Only, the following note in documentation rings a small bell:

"For a collection with inverse="true" (the standard bidirectional one-to-many relationship idiom, for example) we can add elements to a bag or list without needing to initialize (fetch) the bag elements!"

So, if Your code like
Code:
Parent p = (Parent) sess.Load(typeof(Parent), id);
    Child c = new Child();
    c.Parent = p;
    p.Children.Add(c);  //no need to fetch the collection!
    sess.Flush();

does not make child persisted, You should report a bug propably..

Gert

_________________
If a reply helps You, rate it!


Last edited by gert on Wed Jun 14, 2006 2:45 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 10:24 am 
Expert
Expert

Joined: Fri May 13, 2005 5:56 pm
Posts: 308
Location: Santa Barbara, California, USA
i see you are using the second-level cache. are you sure the item is not being persisted? have you checked the database? have you tried disabling the cache or reloading the parent object from the db? basically, with the second-level cache, you have to be sure that you're updating the in-memory object, or calling session.Update()/session.Lock() to sync the cache with the db. the key is to check the db to be sure the data hasn't been persisted.

-devon


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 5:49 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi Devon,

I have double checked that the new object is *not* being persisted to the database.

When degbugging, if I break just after the line of code where the object gets added to the collection, then I see that the new object actually gets added to a collection called QueuedAddsCollection on the NHibernate.Collections.Bag.

If I break before the add and inspect the bag (which triggers a load) then the new object gets added to the bag directly and then persisted correctly to the DB.

Cheers,

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 6:22 pm 
Expert
Expert

Joined: Fri May 13, 2005 5:56 pm
Posts: 308
Location: Santa Barbara, California, USA
you can submit a bug here:

http://jira.nhibernate.org/secure/Dashboard.jspa

but i'm still not convinced that you have a bug only because i have the same configuration in several areas of my domain and the persistence is working fine. one thing i see is that you are calling session.Load(0 and then session.Flush(). i don't see the rest of the code so i assume you aren't using the NHibernate ITransaction interface. if this is true, and you are managing the ADO.NET transactions yourself, then you will have to commit the transation explicitly with something like:

session.Flush();
myADOTran.Commit();

one thing you could try would be to re-write your code to wrap the code in a transaction. if this is a long running Unit of Work then you can call session.Flush() within that tran and things will work as you expect:

Code:
ITransaction tx = session.BeginTransaction();

Parent p = (Parent) sess.Load(typeof(Parent), id);
Child c = new Child();
c.Parent = p;
p.Children.Add(c);  //no need to fetch the collection!

session.Flush();

// do some more work

session.Flush();

// and still some more work

try {
    session.SaveOrUpdate(p);
    tx.Commit();
} catch (Exception ex) {
    tx.Rollback();
    throw ex;
}




If the Unit Of Work is as simple as the code you prodived for demo purposes, then just call SaveOrUpdate() and commit the tran within a try/catch block:

Code:
ITransaction tx = session.BeginTransaction();

Parent p = (Parent) sess.Load(typeof(Parent), id);
Child c = new Child();
c.Parent = p;
p.Children.Add(c);  //no need to fetch the collection!

try {
    session.SaveOrUpdate(p);
    tx.Commit();
} catch (Exception ex) {
    tx.Rollback();
    throw ex;
}


-devon


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 6:52 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi Devon,

I think your reply was in response to a post from Gert as I did not provide any code for the loading or saving of my objects.

I have a helper class that manages the whole transactional side of things. My service layer methods call the appropriate helper method which automatically begins a transaction on the NH session and then commits or rollbacks if an exception occurs:

Code:
        public static void Commit(System.Data.IsolationLevel isolationLevel)
        {
            ITransaction transaction = null;

            try
            {
                transaction = Session.BeginTransaction(isolationLevel);
                Session.Flush();
                transaction.Commit();
            }

            catch (Exception)
            {
                transaction.Rollback();
                throw;
            }

            finally
            {
                if (transaction != null)
                {
                    transaction.Dispose();
                }
            }

        }


The one thing that springs to mind is that I am not explicitly calling Session.Save(child) because I have cascade="all-delete-orphan" in the mapping file. Instead I call Session.Update(parent) and expect the new & updated children to be cascaded automatically...is that OK?

I am totally open to there being a problem with my configuration (in fact I expect it to be my fault!) but I am having trouble finding out what it might be!!

Thanks,

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 7:48 pm 
Expert
Expert

Joined: Fri May 13, 2005 5:56 pm
Posts: 308
Location: Santa Barbara, California, USA
Quote:
I think your reply was in response to a post from Gert as I did not provide any code for the loading or saving of my objects.


you are right. sorry about that. just call me space cadet....

Quote:
The one thing that springs to mind is that I am not explicitly calling Session.Save(child) because I have cascade="all-delete-orphan" in the mapping file. Instead I call Session.Update(parent) and expect the new & updated children to be cascaded automatically...is that OK?


absolutely. in fact, i leverage this model heavily in our ASP.NET app so that i can make many modifications to the parent object, and child objects in various collections, then call SaveOrUpdate() within a transaction on the parent object and let everything cascade. this is a cheap way of getting around some of the business requirements that act upon many different domain objects which might normally require me to expose NH to the BLL. something i don't really want to do.

incidentally, in your helper class you don't have to call Session.Flush() prior to transaction.Commit(). Hibernate explicitely states that the session is automatically flushed upon transaction.Commit(). not really a problem for you but extra code nonetheless.

everything i see here tells me that what you have should be working, so perhaps there really is a bug. if you submit a bug, be sure to include test cases, etc. sorry that i couldn't solve this on for you.

-devon


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 7:55 pm 
Expert
Expert

Joined: Fri May 13, 2005 5:56 pm
Posts: 308
Location: Santa Barbara, California, USA
sorry, one more thing... it is true, that you must wrap your object updates and subsequent call to Save()/SaveorUpdate() in a transaction for the second-level cache to be updated....


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 8:45 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hi Devon,

Thanks for the tip on the session flush...I have removed that from my helper.

Quote:
you must wrap your object updates and subsequent call to Save()/SaveorUpdate() in a transaction for the second-level cache to be updated


Just to be clear on your above comment, do I have to begin the transaction before I call the Save/Update? Also, when you say "object updates", do you mean the actual setting of property values on the object?

My service layer currently calls the Save/Update on the session and then the helper begins the transaction afterwards. This seems to be working OK (with the second level cache being updated correctly) except for the isolated problem that I am attempting to resolve.

Also, when you say be sure to include test cases, do you just mean sample code or is it more elaborate than that?

Cheers,

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 10:32 pm 
Expert
Expert

Joined: Fri May 13, 2005 5:56 pm
Posts: 308
Location: Santa Barbara, California, USA
Quote:
do I have to begin the transaction before I call the Save/Update?

yes. and besides, you sort of want that wrapped in a trannie anyway. if the Save/Update fails, by rule you have to abandon all changes and the NH session.

Quote:
Also, when you say "object updates", do you mean the actual setting of property values on the object?

no, that wasn't very clear. sorry...

Quote:
Also, when you say be sure to include test cases, do you just mean sample code or is it more elaborate than that?

sample code is good enough. something the NH team can load up and run to see the problem.

-devon


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 10:44 pm 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
Hmmm...that is a little problematic for me. I was under the impression that the NH session just tracked all the object changes nicely and that the transaction was only really required for the commit stage. I see I am going to have to re-think a few things then. I wonder if this is the cause of the problem I am experiencing.

I might create a new project with a really simplified domain & app and just double check this parent/child thing out to see if it is something screwy that I am doing. I will eliminate my helper too and create a real bare bones app to confirm the problem before I report anything to the NH team.

Thanks for your help.

Jason


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 13, 2006 10:47 pm 
Expert
Expert

Joined: Fri May 13, 2005 5:56 pm
Posts: 308
Location: Santa Barbara, California, USA
i use a _really_ basic facade to the NH session api. here is my wrapped SaveOrUpdate() method:

Code:
public void SaveOrUpdate(object obj)
{
   ITransaction t = this.dbSession.BeginTransaction();

   try
   {
      this.dbSession.SaveOrUpdate(obj);
      t.Commit();
   }
   catch (Exception ex)
   {
      t.Rollback();
      throw ex;
   }
}


try this method in your new project and see what we see.

-d


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 14, 2006 2:11 am 
Senior
Senior

Joined: Sat Sep 03, 2005 12:54 am
Posts: 139
OK...I have updated my helper so that it creates the transaction before Save or Update on any objects in the session and the problem still remains with the new child object not getting persisted to the DB.

I set lazy="false" on the bag and it works OK so it is feeling like a combination of the second level cache and the lazy loading.

Anyway, I will create the test project and report back soon!

Jason


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 19 posts ]  Go to page 1, 2  Next

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.