Member Menu
 
 Monthly JBoss newsletter:
 
Hibernate Books
CaveatEmptor

Hibernate inside PicoContainer

Integrating hibernate into PicoContainer is really trivial (as almost everything inside pico)

Create Your custom configuration datasource configuration

Subclass net.sf.hibernate.cfg.Configuration.

public class ProjectConfiguration extends Configuration {
    private final static Log _log = LogFactory.getLog(ProjectConfiguration.class);

    /**
     * configure from supplied properties path
     *
     * @param propertiesPath          Description of Parameter
     * @exception HibernateException  Description of Exception
     * @exception IOException         Description of Exception
     */
    public ProjectConfiguration(String propertiesPath) throws HibernateException, IOException {
        if (_log.isDebugEnabled()) {
            _log.debug("configuring from properties: " + propertiesPath);
        }
        Properties hibernateProperties = new Properties();
        hibernateProperties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(propertiesPath));
        addProperties(hibernateProperties);

        addClass(ProjectData.class);
        if (_log.isDebugEnabled()) {
            _log.debug("done.");
        }
    }

}

This class receives as parameter path to properties, configures itself and registers persistent classes. You could of course supply DOM tree or whatever Configuration eats. Or do everything manually (but we like external configuration)

Create Session Provider

SessionProvider will receive Configuration as constructor parameter, build its own Session Factory and provide sessionf for those who cares.

Here is example of session provider implementing thread local pattern:

public class ThreadLocalSessionProvider implements SessionProvider {

    private static Log _log = LogFactory.getLog(ThreadLocalSessionProvider.class);

    ThreadLocal     _session = new ThreadLocal();
    ThreadLocal     _transaction = new ThreadLocal();
    SessionFactory  _factory;

    /**
     * Constructor for the ThreadLocalSessionProvider object
     *
     * @param sfp  Description of Parameter
     */
    public ThreadLocalSessionProvider(Configuration cfg) {
        _factory = cfg.buildSessionFactory();
    }

    /**
     * Gets the Session attribute of the DefaultSessionProvider object
     *
     * @return                        The Session value
     * @exception HibernateException  Description of Exception
     */
    public Session getSession() throws HibernateException {
        Session sess = (Session) _session.get();
        if (sess == null) {
            sess = getFactory().openSession();
            Transaction tr = sess.beginTransaction();
            _session.set(sess);
            _transaction.set(tr);
            if (_log.isDebugEnabled()) {
                _log.debug("created session and started new transaction");
            }
        }
        return sess;
    }

    /**
     * rollback current transaction. shall be called in case of any hibernate
     * exception
     *
     * @exception HibernateException  Description of Exception
     */
    public void rollback() throws HibernateException {
        Transaction tr = (Transaction) _transaction.get();
        if (tr != null) {
            tr.rollback();
        }
        _transaction.set(null);
    }

    /**
     * give session back without disconnecting
     *
     * @param sess  session to be returned
     */
    public void returnSession(Session sess) {
    }

    /**
     * reset session if any
     */
    public void resetSession() {
        _session.set(null);
        _transaction.set(null);
    }

    /**
     * give session back and close it
     *
     * @param sess                    session to be closed
     * @exception HibernateException  may be thrown by hibernate
     */
    public void returnCloseSession(Session sess) throws HibernateException {
        Transaction tr = (Transaction) _transaction.get();
        if (tr != null && !tr.wasCommitted() && !tr.wasRolledBack()) {
            tr.commit();
            _transaction.set(null);
        }
        sess.close();
        _session.set(null);
        if (_log.isDebugEnabled()) {
            _log.debug("returned session and closed it");
        }
    }

    /**
     * Gets the Factory attribute of the BaseSessionProvider object
     *
     * @return   The Factory value
     */
    SessionFactory getFactory() {
        return _factory;
    }

}

Strictly speaking, it's not necessary to use thread local, because this component would be typically registered in request-level container or container can provide thread local semantics.

And creation of session factory is better placed in separate component, since session factory is invariant and can be easily shared.

Now create you DAO

Your dao shall depend (have constructor parameter) of type SessionFactoryProvider. The pico will do the right thing. Mine DAO looks like this

/**
 * hibernate implementation of project manager
 *
 * @author    konstantin
 * @created   February 13, 2004
 * @version   $Revision: 1.3 $
 */
public class HibernateProjectManager implements ProjectManager {
    private final static Log _log = LogFactory.getLog(HibernateProjectManager.class);

    ProjectAuthContext _authContext;
    SessionProvider _sessionProvider;

    /**
     * instantiate project manager off session provider and auth context wi ring
     * is done by container.
     *
     * @param sessionProvider  Description of Parameter
     * @param authContext      Description of Parameter
     */
    public HibernateProjectManager(SessionProvider sessionProvider, ProjectAuthContext authContext) {
        if (_log.isDebugEnabled()) {
            _log.debug("instantiated:" + authContext + " / " + sessionProvider);
        }
        _sessionProvider = sessionProvider;
        _authContext = authContext;
    }

    /**
     * create new project
     *
     * @param name                         Description of Parameter
     * @param description                  Description of Parameter
     * @param client                       Description of Parameter
     * @return                             Description of the Returned Value
     * @exception ProjectStorageException  Description of Exception
     */
    public Project createProject(String name, String description, String client) throws ProjectStorageException {

        try {
            ProjectData pd = new ProjectData();

            pd.setUser(_authContext.getUser());
            pd.setGroup(_authContext.getStatusGroup());
            pd.setName(name);
            pd.setDescription(description);
            pd.setClient(client);

            _sessionProvider.getSession().save(pd);

            return pd;
        } catch (Exception ex) {
            _log.error("exception while creating new project", ex);
            try {
                _sessionProvider.rollback();
            } catch (HibernateException ee) {
                _log.error("error while session rollback", ee);
            } finally {
                _sessionProvider.resetSession();
            }
            throw new ProjectStorageException(ex);
        }
    }

Launch it

In my testcase I do following:

Configure container

    /**
     * rig up pico container and recreate schema
     *
     * @exception Exception  Description of Exception
     */
    public void setUp() throws Exception {
        _container = new DefaultPicoContainer();

        _container.registerComponentImplementation(ProjectConfiguration.class,
            ProjectConfiguration.class,
            new Parameter[]{new ConstantParameter(new String("/connection.properties"))});

        _container.registerComponentImplementation(DefaultSessionFactoryProvider.class);
        _container.registerComponentImplementation(ThreadLocalSessionProvider.class);
        _container.registerComponentImplementation(TestAuthContext.class);
        _container.registerComponentImplementation(HibernateProjectManager.class);

        SchemaExport ex = new SchemaExport((Configuration) _container.getComponentInstance(ProjectConfiguration.class));

        ex.drop(true, true);
        ex.create(true, true);

        _sessionProvider = (SessionProvider) _container.getComponentInstance(ThreadLocalSessionProvider.class);
    }

...and use it ...


    /**
     * The JUnit setup method
     *
     * @exception Exception  Description of Exception
     */
    public void setUp() throws Exception {
        super.setUp();

        _manager = (ProjectManager) _container.getComponentInstance(HibernateProjectManager.class);
    }

This got me fully configured DAO ready to action

    /**
     * A unit test for JUnit
     *
     * @exception Exception  Description of Exception
     */
    public void testProjectCreation() throws Exception {

        Project project = _manager.createProject("foo", "bar", "baz");
        assertNotNull(project);
        assertNotNull(project.getId());
        assertEquals("foo", project.getName());
        assertEquals("bar", project.getDescription());
        assertEquals("baz", project.getClient());

        Collection projects = _manager.getProjects();
        assertEquals(1, projects.size());

        assertTrue(projects.contains(project));
    }

Conclusion

It took us only 3 really trivial classes, to develop fully functional hibernated DAO. And we have flexible configuration, we can have several instances of those services, thy can be located easily etc.

You can try it at home without any harm


  NEW COMMENT

My uncertainty idea about PicoContainer + Hibernate 17 Feb 2004, 15:28 limo
Hi, Konstantin Priblouda, I am going here and gald to wirte my thought.

First of all, I'll apologize for my poor English because I'm Chinese 
and English is not my native. 

I have been developing a framework with Hibernate and PicoContainer.

For example, we develop a UserDao. That is an interface.

interface UserDao {
  User createUser(User u);
  boolean deleteUser(User u);
  ...
}

Sometimes we may need different implementations. UserDaoHibernate or 
UserDaoJdbc ..

We must provide a Session or a Connection to UserDao and supply 
transaction.

so, we make another two interface

interface TxManager {
 void beginTrans();
 void commit();
 void rollback();
}

interface DatabaseAdapter {
 Object get();
}

The TxManager is easy to understand. Whenever we use Hibernate or 
JDBC, we both need transaction support while the transaction is in 
different implementation.


The DatabaseAdapter is used to provide Connection or Session. So we 
return Object and let concrete class choose and cast. Also it contain 
the ThreadLocal management and the only one SessionFactory of the 
System.

class UserDaoHibernate() {
  public UserDaoHibernate(DatabaseAdapter adapter){ 
   this.adapter = adapter;
}
}

Just like the UserDaoHibernate above, the UserDaoJdbc also have a 
constructer with parameter DatabaseAdapter

One can build the components like this:

DefaultPicoContainer container = new DefaultPicoContainer();
container.registerComponentImplementation(UserDao.class, 
UserDaoHibernate.class);
container.registerComponentImplementation(DatabaseAdapter.class, 
DatabaseAdapterForSession.class);

UseDao dao = (UserDao)container.getComponentInstance(UserDao.class);

Then you can use dao.

The TxManager is an aspect of Dao, so it need AOP here.

To use TxManager, the TxManager and the UserDao must use the same 
Session or Connection. So the implementation also need DatabaseAdapter:

Class TxManagerHibernate implements TxManager {
    private Session _session;
    private Transaction _transaction;

    public TxManagerHibernateImpl(DatabaseAdapter adapter) {
        this._session = (Session) adapter.get();
}
…
}

Now I have some problem in AOP + PicoContainer. I try to use dynamic 
proxy to wrap the dao, but it is hard to implement. 

Yesterday I saw some code using the new dynaop in the pico maillist.It 
may be use in my project.

But, I have a problem: 
 when and where to initialize the PicoContainer? I need some 
components  in global , but some others partial. So when should I 
create the pico instance.

The code is under developments now. I haven’t done the whole code yet. 

Sorry again for my poor English. Am I make myself understood?

I hope anyone who can talk and discuss it with me. You will be the 
most helpful. 

Please contact: limo (at) staff (dot) zotn.com or ibingyun (at) sohu (dot) com
 
Re: My uncertainty idea about PicoContainer + Hibernate 18 Feb 2004, 12:13 ko5tik
>But, I have a problem:
> when and where to initialize the PicoContainer? I need some
>components  in global , but some others partial. So when should I
>create the pico instance.

>The code is under developments now. I haven’t done the whole code
yet. 

>Sorry again for my poor English. Am I make myself understood?

>I hope anyone who can talk and discuss it with me. You will be the
>most helpful.

It depends of your environment. 

There is nanocontainer integration in web-tier & webwork 1.x
Works fine for me. ( Basically you get 3 containers - application ,
session & request scope ) 

Request scope basically implements thread local semantics, since it's
kicked at the end of request. 

And you can move thread-local semantics out of your "session provider" -
it can be pico component adapter, or special refenrence kind.
 
© Copyright 2006, Red Hat Middleware, LLC. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc. [Privacy Policy]