k...here goes...
Code:
package com.foo.bar.data.access;
import com.foo.bar.data.exception.DataException;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.HibernateException;
public class DataAccess {
public static final DataAccessWrapper getDataAccessWrapper() {
return new DataAccessWrapperImpl();
}
public static final void initialize(Object o)
throws DataException {
try {
Hibernate.initialize(o);
} catch(HibernateException he) {
throw new DataException(he);
}
}
}
Code:
package com.foo.bar.data.access;
import com.foo.bar.data.exception.DataException;
import net.sf.hibernate.Session;
public interface DataAccessWrapper {
public Session getDataAccessObject() throws DataException;
public void beginTransaction() throws DataException;
public void cleanupDataAccessObject() throws DataException;
public void commitTransaction() throws DataException;
public void replicate(Object replicant) throws DataException;
public void rollbackTransaction();
public void save(Object object) throws DataException;
}
Code:
package com.foo.bar.data.access;
import com.foo.bar.common.Logger;
import com.foo.bar.data.exception.DataException;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;
import java.io.Serializable;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public abstract class AbstractDataAccessWrapper
implements DataAccessWrapper,
Serializable {
private static SessionFactory FACTORY = null;
private static final String FACTORY_JNDI_NAME =
"java:/hibernate/HibernateFactory";
protected AbstractDataAccessWrapper() {}
public static final synchronized SessionFactory getFactory()
throws HibernateException {
Logger.LOG.debug("getFactory");
if(FACTORY == null) {
Logger.LOG.debug("makeFactory");
try {
Context context = new InitialContext();
FACTORY = (SessionFactory)context.lookup(FACTORY_JNDI_NAME);
} catch(NamingException ne) {
Logger.LOG.warn("Not in JNDI context!", ne);
}
if(FACTORY == null) {
Configuration cfg = new Configuration();
cfg.configure();
FACTORY = cfg.buildSessionFactory();
}
}
return FACTORY;
}
protected Session makeSession()
throws HibernateException,
DataException {
Logger.LOG.debug("makeSession");
SessionFactory factory = getFactory();
Session session = factory.openSession();
try {
//Guard against stale JDBC connections and bad connection pools
while(!session.isConnected() || session.connection()
.isClosed()) {
Logger.LOG.warn("New session reconnect!");
session.reconnect();
}
} catch(SQLException sqle) {
throw new DataException(sqle);
}
return session;
}
}
Code:
package com.foo.bar.data.access;
import com.foo.bar.common.Logger;
import com.foo.bar.data.exception.DataException;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.ReplicationMode;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import java.sql.SQLException;
public class DataAccessWrapperImpl
extends AbstractDataAccessWrapper {
private transient Session session = null;
private transient Transaction transaction = null;
public DataAccessWrapperImpl() {
super();
}
public synchronized Session getDataAccessObject()
throws DataException {
Logger.LOG.debug("getDataAccessObject");
try {
if(session == null) {
session = makeSession();
}
} catch(HibernateException he) {
throw new DataException("Problem getting DAW", he);
}
return session;
}
public synchronized void beginTransaction()
throws DataException {
if((transaction == null) && (session != null)) {
try {
transaction = session.beginTransaction();
} catch(HibernateException he) {
throw new DataException(
"Problem beginning hibernate transaction!",
he
);
}
} else {
Logger.LOG.warn("Overlapping Begin Transactions!");
}
}
/**
* Description of the Method
**/
public synchronized void cleanupDataAccessObject() {
if(session != null) {
Logger.LOG.debug("BEGIN cleanupDataAccessObject");
Session tmpSession = session;
session = null;
try {
tmpSession.flush();
} catch(HibernateException he) {
Logger.LOG.warn("Problem flushing session in DAW", he);
}
try {
tmpSession.disconnect();
} catch(HibernateException he) {
Logger.LOG.warn("Problem disconnecting session in DAW", he);
}
try {
tmpSession.close();
} catch(HibernateException he) {
Logger.LOG.warn("Problem closing session in DAW", he);
}
}
Logger.LOG.debug("COMPLETED cleanupDataAccessObject");
}
public synchronized void commitTransaction()
throws DataException {
if(transaction != null) {
try {
session.flush();
transaction.commit();
transaction = null;
session.clear();
} catch(HibernateException he) {
throw new DataException(
"Problem committing hibernate transaction!",
he
);
}
} else {
throw new DataException("Overlapping Commit Transactions!");
}
}
public synchronized void replicate(Object replicant)
throws DataException {
if(session != null) {
try {
session.replicate(
replicant,
ReplicationMode.LATEST_VERSION
);
} catch(HibernateException he) {
throw new DataException(
"Problem replicating object: " +
replicant,
he
);
}
} else {
throw new DataException("Replicate attempted without session!");
}
}
public synchronized void rollbackTransaction() {
Logger.LOG.fatal("Rollback Transaction!");
try {
if(transaction != null) {
transaction.rollback();
}
} catch(HibernateException he) {
Logger.LOG.warn("Problem rolling back Hibernate transaction!", he);
}
transaction = null;
session.clear();
}
public synchronized void save(Object object) throws DataException {
if(session != null) {
try {
session.saveOrUpdate(object);
} catch(HibernateException he) {
throw new DataException("Problem saving object: " + object, he);
}
} else {
throw new DataException("Save attempted without session!");
}
}
}
Typical usage in the business layer looks something like this:
Code:
DataAccessWrapper daw = null;
try {
daw = DataAccess.getDataAccessWrapper();
Session session = daw.getDataAccessObject();
...do stuff with session...
} catch(DataException de) {
....
} catch(HibernateException he) {
throw new BusinessLogicException("FUBAR in business layer!", he);
} finally {
if(daw != null) {
daw.cleanupDataAccessObject();
}
}
I have an abstract base for DataAccessWrapperImpl because I used to subclass it for different environments. (I used to have a version that implemented javax.servlet.http.HttpSessionBindingListener so it could clean itself up when the http session was removed. Don't do that anymore, though...)
This may be a bit of overkill, but I like abstracting my application layers as much as possible. I didn't go so far as to completely shield my business layer from Hibernate. That _would_ be overkill! But this accomplishes the maximum shielding with the minimum code, methinks. I don't think I'll ever move away from Hibernate. (It's just too frikkin' kewl!) But...I'm paranoid. :)