When you are working with Hibernate there is a big chance you have (or will) run into the lazy loading exception: org.hibernate.LazyInitializationException: failed to lazily initialize a collection - no session or session was closed. Well, last week it was my turn. Luckiliy there are lots and lots of articles and blogs about it on the net so in a short time you will have enough information about it to fill at least a day or two by reading them.
In our case we had in one of our objects a OneToMany relation with a child object and it's fetchType was set to EAGER. Now this doesn't have to be a bad choice but in our case it wasn't always necessary to collect the children when the parent was created. So I just set the fetchType to LAZY and tested it... And there it was: the LazyinitializationException.
So apparently we were trying to load the Object children while there was no (Hibernate) session anymore to which the parent object was attached. After a short search it appeared that the cause of this problem lay in the Transaction demarcation we had defined in Spring (we are using the combination Spring-Hiberate in our project).
We have a 'classic' layer structure in our components going from business layer -> service layer -> dao layer. Now we had transactions defined at both business layer and service layer. This caused that some objects were created in one transaction, that transaction ended also ending the Hibernate session, and then the object was used, so it tried to lazy load its children but without a Hibernate session.... et voila: the lazy loading exception. So we did a redesign of our transaction demarcation and this time we only started and ended transactions in our business layer: the correct place according to us. This made the transactional behaviour much more clear but also lead to another problem.
A lot of our unit-test cases are testing at the service layer level. Some of them were failing now since there was no transaction defined at this level. So we decided to make each test method runnning in its own transaction. But we didn't want to have to declare all our test classes in the Spring context. So we made a base test class that sets the transactions programmatically. To have this working with TestNG 5.0 we made the following base class:

JAVA:
  1. import net.pascalalma.util.MyContext;
  2. import org.apache.commons.logging.Log;
  3. import org.apache.commons.logging.LogFactory;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. import org.springframework.orm.hibernate3.HibernateTransactionManager;
  6. import org.springframework.transaction.TransactionStatus;
  7. import org.springframework.transaction.support.DefaultTransactionDefinition;
  8. import org.testng.annotations.AfterMethod;
  9. import org.testng.annotations.BeforeMethod;
  10.  
  11. /**
  12. * Use this class as base class for your test if you need transactional context
  13. * in your tests. If this is not needed you should use CoreTest as base class.
  14. * A transactional is started before the test-method is ran and the transaction will
  15. * be rolled back when the test method has finished. If you need the data to be committed
  16. * to the database you can call 'setComplete()'. This will perform a commit on the
  17. * current transaction.
  18. *
  19. * @author Pascal Alma
  20. *
  21. */
  22. public class TransactionalCoreTest {
  23.  
  24.    private static final Log LOG = LogFactory.getLog(TransactionalCoreTest.class);
  25.  
  26.    private HibernateTransactionManager mgr = null;
  27.    private TransactionStatus status = null;
  28.  
  29.    /**
  30.     * Starts a transaction.
  31.     */
  32.    @BeforeMethod
  33.    protected void setUp() {
  34.       // Obtaining the Spring context
  35.       ClassPathXmlApplicationContext ctx = MyContext.getContext();
  36.       // Set the transactionManager
  37.       mgr = (HibernateTransactionManager) ctx.getBean("transactionManager");
  38.       // Start the transaction
  39.       status = mgr.getTransaction(new DefaultTransactionDefinition());
  40.  
  41.       LOG.debug("Transaction started");
  42.    }
  43.  
  44.    /**
  45.     * Commits the changes in the current transaction. At default changes are lost as the
  46.     * transaction is rolled back at the end of the testcase.
  47.     *
  48.     */
  49.    protected void setComplete() {
  50.       if (!status.isCompleted()) {
  51.          mgr.commit(status);
  52.       }
  53.    }
  54.  
  55.    /**
  56.     * Ends the current transaction by rolling back the changes (if any).
  57.     *
  58.     */
  59.    @AfterMethod
  60.    protected void tearDown() {
  61.       if (!status.isCompleted()) {
  62.          mgr.rollback(status);
  63.       }
  64.       LOG.debug("Transaction ended");
  65.    }
  66. }

So, if we have a test class that needs transactional behaviour we extend from this base class. If we are testing at business layer level, we extend a test base class without this transactional behaviour. This is working fine for us for now, but if you think your solution is better, please let me know! I am always willing to learn new things, especially if these thing are better then the ones I know :-)