Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: Open Session in View and testing
PostPosted: Sun Mar 21, 2004 12:53 pm 
Regular
Regular

Joined: Wed Mar 03, 2004 9:38 am
Posts: 70
Hello

I've been using the Open Session in View pattern, specifically the implementation found in org.springframework.orm.hibernate.support.OpenSessionInViewFilter, and in general I'm happy with it.

However, testing my hibernate code outside the servlet container is difficult because of this. Basically, I have to traverse the object graph in my service layer (where the appropriate methods are declared transactional with springs declarative transaction support, and thus spring automatically opens a session there if one is not already opened), or else I get a LazyInitializationException in my test code. This sort of defeats the purpose of using OpenSessionInViewFilter in the first place.

What I guess I really would like to do, is to open a session in the setUp() method of the TestCase, and then somehow tell spring to use this session and not close it (thus making lazy loading work outside the transactional methods) since I would manually close the session in the tearDown() method of the TestCase. Is there any way to do this?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 22, 2004 3:01 am 
Senior
Senior

Joined: Wed Aug 27, 2003 6:04 am
Posts: 161
Location: Linz, Austria
There is:

Code:
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
try {
...
}
finally {
  TransactionSynchronizationManager.unbindResource(sessionFactory);
  SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);
}


Of course, you can put the snippet before the try into a TestCase.setUp method, and the stuff in the finally block into a TestCase.tearDown implementation. You just need to use the SessionHolder returned by unbindResource then, as you don't have the session reference available:

Code:
protected void setUp() throws Exception {
  Session session = SessionFactoryUtils.getSession(sessionFactory, true);
  TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}

protected void tearDown() throws Exception {
  SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
  SessionFactoryUtils.closeSessionIfNecessary(sessionHolder.getSession(), sessionFactory);
}


Note that the above is exactly what OpenSessionInViewFilter is doing internally: Have a look at the source code if you're interested. It simply uses Spring's lower-level transaction and resource management APIs to bind a Session to respectively unbind a Session from the thread, auto-detected there by transaction managers and Hibernate data access code.

Juergen


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 22, 2004 7:09 am 
Regular
Regular

Joined: Wed Mar 03, 2004 9:38 am
Posts: 70
Thanks, it works like a charm! In case somebody else is wondering about the same thing, here is the testcase class which I extend when I want to use the application context.

Code:
public abstract class AppContextTestCase extends TestCase {
       
    protected FileSystemXmlApplicationContext context = null;
    protected SessionFactory sessionFactory = null;
       
        /* (non-Javadoc)
          * @see junit.framework.TestCase#setUp()
          */
         protected void setUp() throws Exception {
             super.setUp();
             String[] contextLocations = new String[2];
             contextLocations[0] = "web/WEB-INF/applicationContext.xml";
             contextLocations[1] = "test/dataSource-local.xml";
             context = new FileSystemXmlApplicationContext(contextLocations);
         sessionFactory = (SessionFactory) context.getBean("sessionFactory");
         Session session = SessionFactoryUtils.getSession(sessionFactory, true);
         TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
         }

         /* (non-Javadoc)
          * @see junit.framework.TestCase#tearDown()
          */
         protected void tearDown() throws Exception {
         SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactor
y);
         SessionFactoryUtils.closeSessionIfNecessary(sessionHolder.getSession(), sessionFactory);
         context = null;
                 super.tearDown();
         }

}



Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 22, 2004 7:16 am 
Regular
Regular

Joined: Wed Mar 03, 2004 9:38 am
Posts: 70
Argh, formatting was wrangled in the previous post, here it is again, hopefully correct this time.

Code:
public abstract class AppContextTestCase extends TestCase {
       
    protected FileSystemXmlApplicationContext context = null;
    protected SessionFactory sessionFactory = null;
       
    /* (non-Javadoc)
     * @see junit.framework.TestCase#setUp()
     */
     protected void setUp() throws Exception {
         super.setUp();
         String[] contextLocations = new String[2];
         contextLocations[0] = "web/WEB-INF/applicationContext.xml";
         contextLocations[1] = "test/dataSource-local.xml";
         context = new FileSystemXmlApplicationContext(contextLocations);
         sessionFactory = (SessionFactory) context.getBean("sessionFactory");
         Session session = SessionFactoryUtils.getSession(sessionFactory, true);
         TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
         }

    /* (non-Javadoc)
     * @see junit.framework.TestCase#tearDown()
     */
    protected void tearDown() throws Exception {
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactor
y);
        SessionFactoryUtils.closeSessionIfNecessary(sessionHolder.getSession(), sessionFactory);
        context = null;
        super.tearDown();
    }

}



And, congratulations to the spring team on the apparently-very-soon-to-be-released spring 1.0.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 23, 2004 4:42 pm 
Newbie

Joined: Mon Feb 09, 2004 10:00 pm
Posts: 15
I am using this setup for my unit tests as well. Just wondering, isn't it necessary to call session.flush() in the teardown method; or, are you actively making this call in your unit test code?

Thanks, chago


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 24, 2004 4:58 am 
Regular
Regular

Joined: Wed Mar 03, 2004 9:38 am
Posts: 70
According to the documentation and my experience as well, committing a transaction automatically flushes the session, so it's not necessary to explicitly flush it.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 24, 2004 10:04 am 
Newbie

Joined: Mon Feb 09, 2004 10:00 pm
Posts: 15
I'm not seeing that behavior. In your tearDown() method, the applicable statements are:

Quote:
Code:
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(sessionHolder.getSession(), sessionFactory);


Neither of these methods invoke session.flush() therefore no data is committed at the time my unit test completes. Also, I am not creating a transaction in my test code, so perhaps that is a difference here. Either way, I think a session.flush() should be added to the tearDown() method.

Does that make sense?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 24, 2004 11:51 am 
Senior
Senior

Joined: Wed Aug 27, 2003 6:04 am
Posts: 161
Location: Linz, Austria
If you want to emulate OpenSessionInViewFilter's behavior in a test case, you shouldn't call flush in tearDown(), as OpenSessionInViewFilter doesn't flush after view rendering either. OpenSessionInViewFilter is made to be used with transactions (or at least individual, explicit flushs) for concrete data access operations.

Juergen


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 13, 2004 4:41 am 
Expert
Expert

Joined: Thu Sep 04, 2003 8:23 am
Posts: 368
I would like to test my DAOs without my service layer that define the transactions.
If I use the sample juergen gave, it seems to me that I will have some troubles as there is no transaction running during my test.
I think this is the problem chago had.
So is there a way to start a transaction inside the setup method and commit it in the tearDown method to "emulate" a service layer on top of my DAOs ?

Thanx

Seb


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 13, 2004 5:40 am 
Senior
Senior

Joined: Wed Aug 27, 2003 6:04 am
Posts: 161
Location: Linz, Austria
scesbron wrote:
So is there a way to start a transaction inside the setup method and commit it in the tearDown method to "emulate" a service layer on top of my DAOs ?


You don't necessarily need to start a transaction: Using the same Hibernate Session is often good enough. See my first posting in this thread, which shows corresponding setUp/tearDown code.

If you still want to start a transaction, use programmatic transaction demarcation, possibly within your test methods. This is exactly the same as programmatic transaction demarcation in application code.

Code:
protected void testSomething() throws Exception {
  PlatformTransactionManager ptm = new HibernateTransactionManager(sessionFactory);
  TransactionStatus status = ptm.getTransaction(new DefaultTransactionDefinition());
  try {
    ...
  }
  catch (Exception ex) {
    ptm.rollback(status);
    throw ex;
  }
  ptm.commit(status);
}


Or use TransactionTemplate with test code in a callback implementation. The following code is equivalent to the above, with the limitation that callback code can just throw RuntimeExceptions (causing a rollback).

Code:
protected void testSomething() throws Exception {
  PlatformTransactionManager ptm = new HibernateTransactionManager(sessionFactory);
  TransactionTemplate tt = new TransactionTemplate(ptm);
  tt.execute(new TransactionCallbackWithoutResult() {
    protected abstract void doInTransactionWithoutResult(TransactionStatus status) {
      ...
    }
  });
}


Juergen


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 13, 2004 6:01 am 
Expert
Expert

Joined: Thu Sep 04, 2003 8:23 am
Posts: 368
Using the same hibernate session is not good enough for me because changes are not applied between two test methods.
We solved this problem by creating a transaction directly on the hibernate session in the setup and commiting this transaction in the tearDown method. We will add a rollback boolean on our class thus we can know in the tearDown if we have to do a commit or a rollback. But as most of our DAOs methods are atomic, if we have an insert/update problem we will commit nothing so I think rollbacking when we have an exception is not mandatory for us.

My last question : Is there some big differences between using HibernateTransactionManager to create a transaction and using session.beginTransaction ?
What is the preferred approach in a Spring project ?

Thanx

Seb


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 29, 2004 2:55 pm 
Newbie

Joined: Tue Aug 26, 2003 9:45 am
Posts: 17
Location: Toronto, Canada
scesbron wrote:

My last question : Is there some big differences between using HibernateTransactionManager to create a transaction and using session.beginTransaction ?
What is the preferred approach in a Spring project ?

Thanx

Seb


If you're using Spring you should definitely be using Spring to demarcate transactions, and not touching the Hibernate transaction APIs. Then Session creation and lifetime can be synchronized to the transactions, and other resources as well...

Colin Sampaleanu


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 22, 2007 3:28 pm 
Newbie

Joined: Mon Jan 22, 2007 3:25 pm
Posts: 2
hmmm.... very interesting..... but where do i have to get that Spring?

_________________
http://www.ranksluosciai.lt/


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 27, 2007 4:43 am 
Newbie

Joined: Fri Jul 20, 2007 6:02 pm
Posts: 12
ignius.sako.kalbam wrote:
hmmm.... very interesting..... but where do i have to get that Spring?


http://www.springframework.org


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jan 12, 2008 11:16 am 
Newbie

Joined: Sat Jan 12, 2008 11:09 am
Posts: 1
It seems spring is a good choice for hibernate users.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 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.