org.eurocarbdb.dataaccess.HibernateEntityManager.java Source code

Java tutorial

Introduction

Here is the source code for org.eurocarbdb.dataaccess.HibernateEntityManager.java

Source

/*
*   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