Hibernate inside PicoContainerIntegrating 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 ProviderSessionProvider 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 DAOYour 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 itIn 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));
}
ConclusionIt 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
|