/* * Copyright (c) 2020 * San Diego Supercomputer Center * University of California, San Diego * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution.* * * Neither the name of the author nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.ngbw.cipres.sdk.dao; import java.io.InputStream; import java.sql.Blob; import java.sql.SQLException; import java.util.Calendar; import java.util.UUID; import org.apache.log4j.Logger; import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Junction; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.transform.DistinctRootEntityResultTransformer; import org.ngbw.cipres.sdk.dao.bean.range.RangeValue; import org.ngbw.cipres.sdk.dao.bean.utils.SortOrder; import org.ngbw.cipres.sdk.dao.bean.utils.SortOrders; /** * An abstract class with various helper methods/operations to interact with database. * * @author Tony Chen */ public abstract class AppDAO implements AutoCloseable { private static final Logger logger = Logger.getLogger(AppDAO.class); protected final String NL = "\n"; protected final String ERR_PARAM_NULL_EMPTY = "Parameter '%1$s' cannot be null or empty."; private String uuid = null; private Session session = null; protected AppDAO ( String uuid, Session session ) { if (session == null) { throw new IllegalArgumentException("Hibernate Session cannot be null."); } this.session = session; this.uuid = (null != uuid) ? uuid : UUID.randomUUID().toString(); printStatistics("INSTANTIATED"); } protected void printStatistics ( String action ) { logger.debug(String.format( "[%s - %s] - [%s].", this.getClass().getSimpleName(), uuid, action)); } /** * Sets the first result and max result limits to the criteria. * * @param criteria * @param firstResult the 1st result (0-base) * @param maxResults maximum number of results */ protected void setLimit ( Criteria criteria, int firstResult, int maxResults ) { if (null != criteria) { criteria.setFirstResult((firstResult <= 0) ? 0 : (firstResult - 1)); if (maxResults > 0) { criteria.setMaxResults(maxResults); } } } /** * Sets the first result and max result limits to the criteria. * * @param criteria * @param firstResult the 1st result (0-base) * @param maxResults maximum number of results */ protected void setLimit ( Criteria criteria, long firstResult, long maxResults ) { setLimit( criteria, (new Long(firstResult)).intValue(), (new Long(maxResults)).intValue()); } /** * Sets the first result and max result limits to the query. * * @param query * @param firstResult the 1st result (0-base) * @param maxResults maximum number of results * @return */ protected Query setLimit ( Query query, int firstResult, int maxResults ) { if (null != query) { query.setFirstResult((firstResult <= 0) ? 0 : (firstResult - 1)); if (maxResults > 0) { query.setMaxResults(maxResults); } } return query; } /** * Sets the first result and max result limits to the query. * * @param query * @param firstResult the 1st result (0-base) * @param maxResults maximum number of results * @return */ protected Query setLimit ( Query query, long firstResult, long maxResults ) { return setLimit(query, (new Long(firstResult)).intValue(), (new Long(maxResults)).intValue()); } /** * Sets sorting order to the criteria. * * @param criteria * @param sortOrder */ protected void setSortOrder ( Criteria criteria, SortOrder sortOrder ) { if (null != criteria && null != sortOrder && !sortOrder.isEmpty()) { for (Order order : sortOrder.getOrders()) { criteria.addOrder(order); } } } /** * Sets sorting orders to the criteria. * * @param criteria * @param theClass * @param sortOrders */ protected void setSortOrders ( Criteria criteria, Class theClass, SortOrders sortOrders ) { if (null != criteria && null != sortOrders && !sortOrders.isEmpty()) { setSortOrder(criteria, sortOrders.getSortOrder(theClass)); } } /** * Sets the criteria's result transformer with * {@code DistinctRootEntityResultTransformer.INSTANCE.} * * @param criteria * * @return */ protected Criteria distinctRootEntityResults ( Criteria criteria ) { if (criteria == null) { throw new IllegalArgumentException("Criteria cannot be null."); } return criteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE); } /** * Sets the criteria's projection to {@code Projections.rowCount()}. * * @param criteria * * @return */ protected Criteria rowCountOnly ( Criteria criteria ) { if (criteria == null) { throw new IllegalArgumentException("Criteria cannot be null."); } return criteria.setProjection(Projections.rowCount()); } /** * Sets range value(s) accordingly to the junction. * * @param junction the Hibernate Conjunction or Disjunction * @param propertyName the property name * @param rangeValue the range values * * @return */ protected Junction setRangeValues ( final Junction junction, final String propertyName, final RangeValue rangeValue ) { if (junction == null) { throw new IllegalArgumentException("Hibernate junction cannot be null."); } else if (propertyName == null || propertyName.trim().isEmpty()) { throw new IllegalArgumentException("Property name cannot be empty or null."); } if (null != rangeValue) { if (rangeValue.isExactEqual()) // Equal, propertyValue = rangeValue { // If the Lower and Upper values of the Range is an instance // of Calendar, cast it and retrieve the 'Date' from it, // otherwise do nothing. junction.add(Restrictions.eq( propertyName, (rangeValue.lowerValue() instanceof Calendar? ((Calendar) rangeValue.lowerValue()).getTime() : rangeValue.lowerValue()))); } else // It contains range values. { if (rangeValue.lowerValue() != null) // has LowerBound { if (rangeValue.isLowerBoundOpen()) // GreaterThan, propertyValue > rangeValue { junction.add(Restrictions.gt( propertyName, (rangeValue.lowerValue() instanceof Calendar? ((Calendar) rangeValue.lowerValue()).getTime() : rangeValue.lowerValue()))); } else // GreaterThanEqual/AtLeast (ge) ==> propertyValue >= rangeValue { junction.add(Restrictions.ge( propertyName, (rangeValue.lowerValue() instanceof Calendar? ((Calendar) rangeValue.lowerValue()).getTime() : rangeValue.lowerValue()))); } } if (rangeValue.upperValue() != null) // has UpperBound { if (rangeValue.isUpperBoundOpen()) // LessThan, propertyValue < rangeValue { junction.add(Restrictions.lt( propertyName, (rangeValue.upperValue() instanceof Calendar? ((Calendar) rangeValue.upperValue()).getTime() : rangeValue.upperValue()))); } else // AtMost, propertyValue <= rangeValue { junction.add(Restrictions.le( propertyName, (rangeValue.upperValue() instanceof Calendar? ((Calendar) rangeValue.upperValue()).getTime() : rangeValue.upperValue()))); } } } } return junction; } /** * Create a new record (row) in database using Session.save() method. * *

* Caller is responsible for closing the Session after the transaction is done. *

* * * @param object * * @return * * @throws HibernateException */ protected T create ( T object ) throws HibernateException { try { initTransaction(); session.save(object); doCommit(); return object; } catch ( HibernateException he ) { doRollback(); throw he; } finally { } } /** * Updates an exist record (row) in database using Session.update() method. * *

* Caller is responsible for closing the Session after the transaction is done. *

* * @param object * * @return * * @throws HibernateException */ protected T update ( T object ) throws HibernateException { try { initTransaction(); session.update(session.merge(object)); doCommit(); return object; } catch ( HibernateException he ) { doRollback(); throw he; } finally { } } /** * Creates a new record (if it doesn't exist) or updates the record (if it exists) in database * using Session.saveOrUpdate(). * *

* Caller is responsible for closing the Session after the transaction is done. *

* * @param object * * @return * * @throws HibernateException */ protected T saveOrUpdate ( T object ) throws HibernateException { try { initTransaction(); session.saveOrUpdate(object); doCommit(); return object; } catch ( HibernateException he ) { doRollback(); throw he; } finally { } } protected T delete ( T object ) throws HibernateException { try { initTransaction(); session.delete(object); doCommit(); return object; } catch ( HibernateException he ) { doRollback(); throw he; } finally { } } // protected void // saveOrUpdate ( Object ... objects ) throws HibernateException // { // if (null != objects && objects.length > 0) // { // try // { // this.initTransaction(); // // for (Object obj : objects) // { // if (null != obj) // { // session.saveOrUpdate(obj); // } // } // // this.doCommit(); // } // catch ( HibernateException he ) // { // this.doRollback(); // throw he; // } // finally // { // // Caller is responsible to closeAndQuitSilently the Session. // } // } // } protected Session getSession () { return session; } /** * @deprecated * @param session */ public void closeQuietly ( Session session ) {} /** * @deprecated * @throws HibernateException */ protected void startTransaction () throws HibernateException {} /** * @deprecated * This method will be removed from future versions. * * @throws HibernateException */ protected void commit () throws HibernateException {} /** * @deprecated * This method will be removed from future versions. * * @throws HibernateException */ protected void rollback () throws HibernateException {} private Session initTransaction () throws HibernateException { if (session == null) { session = AppDAOFactory.getInstance().newSession(); } else if (!session.isOpen()) { session = AppDAOFactory.getInstance().newSession(); } session.beginTransaction(); return session; } /** * Commits the transaction associated with the Session. * *

* This method will not commit the transaction if it has already been committed. *

* * @throws HibernateException */ private void doCommit () throws HibernateException { try { if (null != session && session.isOpen()) { Transaction tx = session.getTransaction(); if (null != tx && tx.isActive() && !tx.wasCommitted()) { tx.commit(); } } } catch ( Throwable t ) { logger.error(t.getMessage(), t); throw new HibernateException(t); } } private void doRollback () throws HibernateException { try { if (null != session && session.isOpen()) { Transaction tx = session.getTransaction(); if (null != tx && tx.isActive() && !tx.wasRolledBack()) { tx.rollback(); } } } catch ( Throwable t ) { logger.error(t.getMessage(), t); throw new HibernateException(t); } } /** * Closes the Session if it is still open. * Only logs the error/exception if there is one. */ public void closeQuietly () { try { if (null != session) { session.close(); } printStatistics("SESS_CLOSED"); } catch ( Throwable t ) { logger.error(t.getMessage(), t); } finally { session = null; } } // /** // * Merges the object in Session using Session.merge() methods. // * // *

// * Caller is responsible for closing the Session after the transaction is done. // *

// * // * @param session // * @param object // * // * @return // * // * @throws HibernateException // */ // public Object // merge ( Session session, Object object ) throws HibernateException // { // try // { // return session.merge(object); // } // catch ( HibernateException he ) // { // throw he; // } // finally // { // //this.closeAndQuitSilently(sess); // // Caller is responsible to closeAndQuitSilently the Session. // } // } public boolean isSessionDirty () { return null != session && session.isDirty(); } public boolean isSessionConnected () { return (null != session) && session.isConnected(); } public boolean isSessionOpen () { return (null != session) && session.isOpen(); } public static synchronized byte[] toBytes ( Blob blob ) throws SQLException { if (null != blob) { int length = (new Long(blob.length())).intValue(); return blob.getBytes(1, length); } return null; } public static synchronized Blob toBlob ( Session session, String data ) throws HibernateException { return (null == data)? null : doCreateBlob(session, data.getBytes()); } public static synchronized Blob toBlob ( Session session, byte[] bytes ) throws HibernateException { return (null == bytes)? null : doCreateBlob(session, bytes); } public static synchronized Blob toBlob ( Session session, InputStream is, long length ) throws HibernateException { if (null == is) { throw new IllegalArgumentException("InputStream cannot be null."); } else if (length <= 0) { throw new IllegalArgumentException("Length must be a positive value greater than 0."); } return doCreateBlob(session, is, length); } private static synchronized Blob doCreateBlob ( Session session, byte[] bytes ) { return Hibernate.getLobCreator(session).createBlob(bytes); } private static synchronized Blob doCreateBlob ( Session session, InputStream is, long length ) { return Hibernate.getLobCreator(session).createBlob(is, length); } }