Spring for .NET Community Forums    

Go Back   Spring for .NET Community Forums > General > NHibernate

Reply
 
Thread Tools Display Modes
  #1  
Old 05-23-2007, 09:08 AM
juflo juflo is offline
Junior Member
Spring User
 
Join Date: May 2007
Location: Paris, France
Posts: 28
Question Usage of HibernateDaoSupport and HibernateTemplate in lazy mode

Hi all.

My question is about usage of HibernateDaoSupport / HibernateTemplate in lazy mode.

I've created a simple dao :

Code:
    public class BookDao : HibernateDaoSupport, IBookDao
    {
        public Book FindById(decimal id)
        {
            Book book = HibernateTemplate.Get<Book>(id);
            return book;
        }
    }
I create the following simple test :
("ctx" is ContextRegistry.GetContext() called during [SetUp])

Code:
        [Test]
        public void Load()
        {
            IBookDao dao = ctx["BookDao"] as IBookDao;
            Assert.IsNotNull(dao);
            Book book = dao.FindById(1);
            Assert.IsNotNull(book);
            BookGroup group = book.BookGroup;
            Assert.IsNotNull(group);
            Assert.IsNotNull(group.Name);
         }
The problem is I get the following exception when executing "BookGroup Assert.IsNotNull(mappinggroup.Mappinggroup)" :
NHibernate.LazyInitializationException: Could not initialize proxy - the owning Session was closed

I use :
- NHibernate 1.2.0 GA
- Spring.Data.NHibernate12-20070521-1128
- an Oracle database (10g)

My configuration files are the same as those in the sample provided in Spring.Data.NHibernate12-20070521-1128.

I have no problem if I set lazy to false, or if I use pure NHibernate in lazy mode (using direct ISession in my dao instead of template).

Sorry if this a "noob" question, but i've searched on the forum, found some posts about this exception, but no solution for me.

Thanks for help !
Reply With Quote
  #2  
Old 05-23-2007, 01:22 PM
juflo juflo is offline
Junior Member
Spring User
 
Join Date: May 2007
Location: Paris, France
Posts: 28
Default

BTW, i read and fully understand this post : http://forum.springframework.net/showthread.php?t=641

But does a work around exist in the case of a Winforms app ?
Reply With Quote
  #3  
Old 05-23-2007, 03:53 PM
swalters swalters is offline
Member
Spring User
 
Join Date: May 2006
Posts: 33
Default

Hi,

Spring is closing the Hibernate session as soon as you leave the Dao call. What I do is wrap my tests in a transaction. There may be other options, but this seems to work OK for us.


using Spring.Transaction.Interceptor;
using Spring.Data.NHibernate.Support;
using Spring.Data.NHibernate;

[Test]
public void TestGetItemByID()
{
HibernateTransactionManager hibernateTransactionManager = (HibernateTransactionManager)ctx.GetObject("hibern ateTransactionManager");

TransactionTemplate tt = new TransactionTemplate(hibernateTransactionManager);

object result = tt.Execute(new TransactionDelegate( delegate {
Item Item = _coreDaos.ItemDao.FindById(1);
Assert.IsNotNull(Item.ItemCode);
Assert.AreEqual("Test Item Description", Item.ItemDesc);
Assert.IsNotNull(Item.Season);
Assert.IsNotNull(Item.UOM);
Assert.IsNotNull(Item.AuxiliaryFieldByName("Text") );
Assert.AreEqual(3, Item.ItemDetails.Count);
ItemDetail itemDetail = (ItemDetail)Item.ItemDetails[new ItemDetailDims("Test Item Dim1","Test Item Dim2","Test Item Dim3")];
Assert.AreEqual("Test Item Dim1", itemDetail.Dims.ItemDim1);
return null;
}));
}

-Shane
Reply With Quote
  #4  
Old 05-23-2007, 04:26 PM
juflo juflo is offline
Junior Member
Spring User
 
Join Date: May 2007
Location: Paris, France
Posts: 28
Default

It's an acceptable workaround for a test-method.
But in case of usage in my winform app, it seems difficult for me to have the sort-of TransactionDelagate scope known, because it depends of the user choices.
I don't mind having "staying alive" connections, if it's possible, as it is when using "pure" NHibernate.
Reply With Quote
  #5  
Old 06-08-2007, 12:58 AM
dharan_k dharan_k is offline
Junior Member
New User
 
Join Date: May 2007
Posts: 8
Default

I think workaround for this is

public class BookDao : HibernateDaoSupport, IBookDao
{
public Book FindById(decimal id)
{
Book book = HibernateTemplate.Get<Book>(id);

// just access for first time loading because of lazy loading.
BookGroup group = book.BookGroup;
log.Debug("group = " + group);

return book;
}
}

Then you don't get this error.
NHibernate.LazyInitializationException: Could not initialize proxy - the owning Session was closed
Reply With Quote
  #6  
Old 06-08-2007, 11:53 AM
steinard steinard is offline
Senior Member
Spring User
 
Join Date: Oct 2006
Location: Bergen, Norway
Posts: 357
Default

Hi!

Just some thoughts on the LazyInitializationException issue:

If you do what Dharan_k suggests then you actually load the collection, and you are able to do this because you are in the DAO and the session is still open. You could just as well have flagged your collection as Lazy=false in your .hbm.xml or attribute if you use that. For the NUnit scenario I would simply have kept the session in the test class to keep things simple and avoid introduced any changes (due to testing purposes) to any other classes.

But in general, if your architecture is a winapp on just one machine with a database (no network trips involved), then you could keep the session somewhere, but then you must remember to empty it's cache or close it at regular intervals or else it will grow too large and consume much memory.

If you are in a three tiered architecture, and in your presentation model (client side) calls a lazy collection you will get the LazyInitializationException, unless the collection was eagerly fetched. I've been looking for good and clean ways of handling these cases but haven't found any real good solutions yet. If you search for ".ben and lazy loading", then you'll find a post where he describes how he solved it by modifying the getter/setter properties of the lazy collection. Personally (and this is also a matter of taste), I do not like that solution since it might give you unintended latency because you are going back to your middleware server to initialize a lazy collection (which might be fine and exactly what you want), but for some API programmer this might not be so good because he/she might not be aware of the networking costs such a design implies (this problem is similar to the latency problem of proxies wanting to achieve location transparency).

Therefore, I currently let all programmers consuming our internal API hit the LazyInitializationException to make them aware of the fact that they would need a network trip to load that specific collection. Thus, we most probably will have a discussion and reflect over the amount of data needed to fulfill the various usecases, and how a facade can load that data in one go (one network trip). The idea is that it might be more efficient to fetch more data first then the cost of going back and forth initializing lazy collections.

Some guidelines we use to help us decide when to use lazy or not:
- Identify data that seldome changes and that may go into a client side cache.
- Load data needed in 90% of the cases for a usecase.
- Never let any network trip cost more then 5 seconds.

Even though we follow these guidelines, we have not found an elegant way of handling the lazy exceptions on the client side. Currently we know the few cases where we expect the exceptions, catch them and loads the collection. It does not feel like a good solution, because similar code is repeated in many different classes, but it works..... At least we have some focus on how to reduce such code.

Please comment and advice my thoughts....


Cheers,
Steinar.

Last edited by steinard; 06-08-2007 at 04:02 PM.
Reply With Quote
  #7  
Old 07-02-2007, 03:43 PM
juflo juflo is offline
Junior Member
Spring User
 
Join Date: May 2007
Location: Paris, France
Posts: 28
Default

Hi steinard

I've read your answer with a lot of interest.
It's been a couple of week now that I play (and work !) with Spring/NHibernate. My situation is a 3 tiers architecture.

My opinion is th following : the developpers needs to ask himself which datas must be return to the client for the current round trip. So it's up to him to force loading using SetFetchMode in the dao layer. This is what I called (not from me) the "unit of work".
If data is missing, then a new service call needs to be done, which means a new round trip to the server.
What's behind that ? Well, the "which datas are return ?" is part of the service returns. So, you must ask yourself this question.

In my case (unit test case), I need to make my class inherited from AbstractTransactionalDbProviderSpringContextTests, which make a Session alive during all the test method.
Reply With Quote
  #8  
Old 07-02-2007, 11:32 PM
steinard steinard is offline
Senior Member
Spring User
 
Join Date: Oct 2006
Location: Bergen, Norway
Posts: 357
Default

Hi!

My thoughts on the lazy load exception is very general and I guess it can be applied to most 3tiered win-applications. What I meant with using facades in your service layer is that you can optimize your datafetching. You really need to test when you should use lazy or not, and there is no correct answer here as it depends on the usecase and how often you might need various data in that use case. In most cases a mix is good, but it should be a reflected design choice, and tested in an authentic setting to see how your latency turns out. Meanwhile, some rules of thumb might guide you along the way.

Quote:
In my case (unit test case), I need to make my class inherited from AbstractTransactionalDbProviderSpringContextTests, which make a Session alive during all the test method.
I do not know your architecture, so you might be right when you say you need to derive from AbstractTransactionalDbProviderSpringContextTests. So far, when it comes to unit tests, I haven't cared about the closing of the sessions. I just wind up my own session that I use to attach detached objects with:

Code:
[TestFixtureSetUp]
public void Init()
{
    dao = ctx["UserDAO"] as IUserDAO;
    session = SessionFactoryUtils.GetSession(dao.SessionFactory, true);
}

[Test]
public void TestLoadingUser()
 {
    IUser user = dao.LoadUserByUserName(typeof(IUser), "MrMySQL");
    ICollection<IRole> roles = user.Roles;
    
    try
    {
        // here I'll get the lazy exception because the session is closed...
        Assert.AreEqual(0, roles.Count);
    } catch(NHibernate.LazyInitializationException e)
    {
        // since owning session is closed, then reconnect it to your own session
        session.Lock(user, LockMode.Read);
        Assert.AreEqual(0, roles.Count);
    }
}

[Test]
public void TestSaveOrUpdateUser()  { ... }

[Test]
public void TestModifyRolesOnUser() { ... }

[Test]
public void TestIsSessionOpen()
{
    Assert.IsTrue(session.IsOpen);
}
http://www.hibernate.org/hib_docs/nh...llections-lazy

Cheers,
Steinar.
Reply With Quote
  #9  
Old 07-03-2007, 05:31 PM
swalters swalters is offline
Member
Spring User
 
Join Date: May 2006
Posts: 33
Default

I've been using the method that jwray outlined in this post on the Spring.Java forum.

http://forum.springframework.org/showthread.php?t=16745


Basically, the domain objects publish any properties that are lazy loaded by default. The client then uses these properties to build a hydration specification to tell the dao layer which properties should be initialized. Each property can be loaded on prefetch or postfetch.

The downside is that the client must specify which properties to load on a call to the service layer. The upside is that you don't have lots of different service and dao methods to read data.

I've got some code examples if anyone is interested.

-Shane
Reply With Quote
  #10  
Old 07-03-2007, 07:03 PM
.ben .ben is offline
Senior Member
Spring User
 
Join Date: Oct 2005
Location: Belgium
Posts: 214
Default

At the moment I'm leaning towards a variant of the approach juflo has given: use other classes in your UI. I.E: the service layer does not return the classes used in the domain/persistence layer but have it return more UI centric classes.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 12:10 AM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.