Java tutorial
/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * Last commit: $Rev: 1552 $ by $Author: glycoslave $ on $Date:: 2009-07-20 #$ */ /** * $Id: HibernateEntityManager.java 1552 2009-07-19 18:36:56Z glycoslave $ * Last changed $Author: glycoslave $ * EUROCarbDB Project */ package org.eurocarbdb.dataaccess; // stdlib imports import java.util.Set; import java.util.Map; import java.util.List; import java.io.Serializable; // 3rd party imports import org.apache.log4j.Logger; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Hibernate; import org.hibernate.Transaction; import org.hibernate.SessionFactory; import org.hibernate.Criteria; import org.hibernate.cfg.Configuration; import org.hibernate.criterion.Projections; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.NonUniqueObjectException; import org.hibernate.metadata.ClassMetadata; import org.hibernate.hql.QueryTranslator; import org.hibernate.hql.QueryTranslatorFactory; import org.hibernate.engine.SessionFactoryImplementor; // eurocarb imports // import org.eurocarbdb.dataaccess.hibernate.HibernateUtil; import org.eurocarbdb.dataaccess.core.Contributor; import org.eurocarbdb.dataaccess.exception.EurocarbException; import static java.util.Collections.emptyMap; import static org.eurocarbdb.util.StringUtils.join; import static org.eurocarbdb.util.JavaUtils.printStackTrace; /** * Class to handle the management of entities from the database using * the Hibernate API. Includes instantiation, population and * deserialisation from state. * * @author mjh * @author hirenj * @version $Rev: 1552 $ */ public class HibernateEntityManager implements EntityManager { /** Logging handle. */ static final Logger log = Logger.getLogger(HibernateEntityManager.class); /** Hibernate SessionFactory singleton, created upon request, * in {@link #init()}. */ private static SessionFactory sessionFactory; /** Has a unit of work been started yet? ie: are we inside of a * {@link Transaction}? Hibernate gets very upset if we aren't, * so it's an error to do anything without one. */ private boolean uowStarted = false; /** Default JNDI config file for Eurocarb connection info */ public static final String Default_Jndi_Config_File = "hibernate.jndi.cfg.xml"; //~~~~~~~~~~~~~~~~~~~~~~~~~~ METHODS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Init hibernate {@link SessionFactory}, this takes a while, but only * needs to be done once per application instance. */ public static void init() { if (sessionFactory != null) throw new RuntimeException("A non-null SessionFactory already exists"); log.info("initialising hibernate..."); boolean use_jndi = Boolean.valueOf(Eurocarb.getProperty("ecdb.use_jndi")); // build SessionFactory if (use_jndi) { log.info("using JNDI to obtain connection params"); initFromJndi(); } else if (Eurocarb.getProperty("ecdb.db.username") != null && Eurocarb.getProperty("ecdb.db.password") != null && Eurocarb.getProperty("ecdb.db.name") != null && Eurocarb.getProperty("ecdb.db.hostname") != null) { log.info("Using application configuration for connection params (ecdb.db.* properties)"); sessionFactory = new Configuration().configure() // use default 'hibernate.cfg.xml' .setProperty("hibernate.connection.username", Eurocarb.getProperty("ecdb.db.username")) .setProperty("hibernate.connection.password", Eurocarb.getProperty("ecdb.db.password")) .setProperty("hibernate.connection.url", "jdbc:postgresql://" + Eurocarb.getProperty("ecdb.db.hostname") + "/" + Eurocarb.getProperty("ecdb.db.name")) .buildSessionFactory(); } else { log.info("using hibernate.properties for connection params"); sessionFactory = new Configuration().configure() // use default 'hibernate.cfg.xml' .buildSessionFactory(); } // check new SessionFactory validity if (sessionFactory == null) throw new RuntimeException("freshly-created sessionFactory was null"); if (log.isDebugEnabled()) { Map<String, ClassMetadata> hash = (Map<String, ClassMetadata>) sessionFactory.getAllClassMetadata(); log.debug("SessionFactory created with " + hash.size() + " entities"); } } static void initFromJndi() { String jndi_conf = Eurocarb.getProperty("ecdb.use_jndi.config"); if (jndi_conf == null || jndi_conf.length() == 0) { log.warn("JNDI URL not given so reverting to default: " + Default_Jndi_Config_File); jndi_conf = Default_Jndi_Config_File; } log.info("trying JNDI configuration from file: " + jndi_conf); sessionFactory = new Configuration().configure(jndi_conf).buildSessionFactory(); log.debug("JNDI config successful"); } /** * Converts the given {@link DetachedCriteria} into a {@link Criteria} * that can be executed. */ public static final Criteria convertToCriteria(DetachedCriteria dc) { return dc.getExecutableCriteria(getSessionFactory().getCurrentSession()); } /** * Returns an SQL query string from the given HQL query string. */ public static final String translateHql2Sql(String hql_query_string) { SessionFactoryImplementor sfi = (SessionFactoryImplementor) sessionFactory; QueryTranslatorFactory qtf = sfi.getSettings().getQueryTranslatorFactory(); QueryTranslator qt = qtf.createQueryTranslator("translated_hql_query", hql_query_string, emptyMap(), sfi); qt.compile(emptyMap(), false); return qt.getSQLString(); } /** * Gets the Hibernate {@link SessionFactory} from which we're * getting our {@link Session}s. */ public static SessionFactory getSessionFactory() { if (sessionFactory == null) { log.debug("sessionFactory == null, dynamically initialising one..."); init(); } return sessionFactory; } /** * Rolls back current transaction, doesn't close Hibernate {@link Session}. * {@inheritDoc} */ public void abortUnitOfWork() { /* if ( ! uowStarted ) throw new EurocarbException( "Unit of work not yet started, cannot abort"); */ Transaction tx = getHibernateSession().getTransaction(); if (tx == null) return; if (!tx.isActive()) { log.warn("a transaction is not active, cannot be aborted"); return; } try { log.warn("aborting current transaction, trying to rollback..."); tx.rollback(); log.debug("transaction successfully rolled back"); } catch (Throwable ex) { log.error("Could not rollback transaction - " + "exception caught during rollback:", ex); } finally { uowStarted = false; } } /** * Opens new (Hibernate-wrapped) transaction. * {@inheritDoc} */ public void beginUnitOfWork() { log.debug("starting database transaction"); getHibernateSession().beginTransaction(); uowStarted = true; } /** * Commits current transaction, rolls back if commit throws Exception. * {@inheritDoc} */ public void endUnitOfWork() { boolean success = false; try { if (getHibernateSession().getTransaction().isActive()) { log.debug("*** committing database transaction ***"); getHibernateSession().getTransaction().commit(); } else { log.warn("no active transaction, cannot be ended"); } success = true; } finally { if (!success) abortUnitOfWork(); log.trace("(closing hibernate session)"); getHibernateSession().close(); uowStarted = false; } } /** * Returns the current Hibernate {@link Session}, or creates one if necessary. * @see {@link SessionFactory} */ public Session getHibernateSession() { return getSessionFactory().getCurrentSession(); } /* @see EntityManager#countAll(Class) */ public <T> int countAll(Class<T> c) { if (log.isTraceEnabled()) log.trace("looking up total count of objects of " + c); Object count = getHibernateSession().createCriteria(c).setProjection(Projections.rowCount()).uniqueResult(); if (count == null) return 0; if (count instanceof Integer) { return ((Integer) count).intValue(); } else if (count instanceof Long) { return ((Long) count).intValue(); } else return 0; } /* @see EntityManager#createNew(Class) */ public <T> T createNew(Class<T> c) { return __instance_of(c); } /* @see EntityManager#flush() */ public void flush() { log.trace("attempting to manually flush data to data store."); if (getHibernateSession().getTransaction().isActive()) { log.debug("*** manually flushing data to data store ***"); getHibernateSession().flush(); } else { log.warn("no active transaction, cannot flush data to store."); } } /* @see EntityManager#createQuery(Class) */ public <T> Criteria createQuery(Class<T> c) { return getHibernateSession().createCriteria(c); } /* @see EntityManager#getQuery(String) */ public Query getQuery(String name_of_query) { return getHibernateSession().getNamedQuery(name_of_query); } /* @see EntityManager#lookup(Class,Serializable) */ @SuppressWarnings("unchecked") // ^^^ unavoidable, due to lack of genericity in Hibernate lib public <T> T lookup(Class<T> c, int object_id) { if (log.isDebugEnabled()) log.debug("attempting to lookup object of " + c.getName() + " with id=" + object_id); try { Session session = getHibernateSession(); T entity = (T) session.get(c, object_id); return entity; } catch (NonUniqueObjectException e) { __log_exception(e, c); throw e; } } /** * Attempts to pre-fetch most of the data for the given object, including * each of its properties and associations. If the passed object is a * {@link Collection} then the whole Collection will be pre-fetched. */ public <T> void populate(T object) { Hibernate.initialize(object); } /* @see EntityManager#lookup(T,Serializable) */ public <T> void lookup(T destinationObject, int objectId) { if (log.isDebugEnabled()) { log.debug("attempting to populate existing object " + destinationObject + " with id=" + objectId); } try { Session session = getHibernateSession(); session.load(destinationObject, objectId); } catch (NonUniqueObjectException e) { __log_exception(e, destinationObject); throw e; } } public <T> void revert(T entity) { Session s = getHibernateSession(); s.evict(entity); } /* @see EntityManager#remove(T) */ public <T> void remove(T entity) { if (log.isDebugEnabled()) log.debug("attempting to remove (detach) object " + entity); Session s = getHibernateSession(); s.delete(entity); } /* @see EntityManager#removeAll */ public <T> void removeAll(Set<? extends T> entities) { for (T e : entities) remove(e); } /* @see EntityManager#store(T) */ public <T> void store(T entity) { if (log.isDebugEnabled()) log.debug("attempting to store (make persistent) object " + entity); Session s = getHibernateSession(); if (entity instanceof EurocarbObject) validate((EurocarbObject) entity); try { s.save(entity); //s.saveOrUpdate( entity ); } catch (NonUniqueObjectException ex) { log.warn("passed " + entity.getClass().getSimpleName() + " was originally loaded in a different Session" + "; attempting to merge changes...", ex); s.merge(entity); log.debug("changes merged ok"); } } /** * Checks that the passed {@link EurocarbObject} is valid, throws * exceptions if it isn't. */ void validate(EurocarbObject x) throws EurocarbException { try { x.validate(); } catch (EurocarbException ex) { log.warn("Caught exception while validating " + x, ex); throw ex; } validateContributor(x); } private void validateContributor(Object x) throws EurocarbException { return; } private void validateContributor(Contributed x) throws EurocarbException { if (x.getContributor() == null) { throw new EurocarbException("Could not validate EurocarbObject, contributor is null"); } } /* @see EntityManager#update(T) */ public <T> void update(T entity) { if (log.isDebugEnabled()) log.debug("attempting to update object " + entity); Session s = getHibernateSession(); s.update(entity); } public <T> void refresh(T entity) { if (log.isDebugEnabled()) log.debug("attempting to refresh object of " + entity.getClass()); Session s = getHibernateSession(); s.refresh(entity); } @Deprecated public void refreshContributor(Contributed entity) { if (entity == null || entity.getContributor() == null || entity.getContributor().getContributorId() < 0) return; Contributor contributor = lookup(Contributor.class, entity.getContributor().getContributorId()); if (contributor != null) entity.setContributor(contributor); } public <T> void merge(T entity) { if (log.isDebugEnabled()) log.debug("attempting to merge object of " + entity.getClass()); Session s = getHibernateSession(); s.merge(entity); } public <T> void storeAll(Set<? extends T> entities) { for (T e : entities) store(e); } /* instantiates an instance of passed class; absorbs exceptions. */ private static final <T> T __instance_of(Class<T> c) { T entity = null; try { entity = c.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } assert entity != null; return entity; } private final void __log_exception(NonUniqueObjectException e, Object entity) { log.warn("Caught NonUniqueObjectException while working with " + entity.getClass().getName() + ". This often means that you need to (re-)implement " + "the equals(Object) & hashCode() methods in this class " + "so that objects in the session can be compared for " + "*equality* rather than *identity*.", e); } } // end class