Java tutorial
/* * Copyright (C) 2012 Jose Enrique Pons Fras <jpons@decsai.ugr.es>. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package es.jpons.persistence; import es.jpons.persistence.constants.OpenInterval; import es.jpons.persistence.exception.TemporalException; import es.jpons.persistence.exception.TemporalInsertException; import es.jpons.persistence.query.AllenRelation; import es.jpons.persistence.query.TemporalQuery; import es.jpons.persistence.util.TemporalHibernateUtil; import es.jpons.temporal.types.PossibilisticVTP; import es.jpons.temporal.types.TemporalPK; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.sql.Connection; import java.util.List; import java.util.Properties; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.hibernate.Criteria; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Restrictions; import org.joda.time.DateTime; import org.joda.time.Duration; /** * Temporal Persistence Manager. This class implements all the persistence * operations for a temporal database. * * @author Jose Enrique Pons Fras <jpons@decsai.ugr.es> First version * 04/10/2012 * */ public class TemporalPersistenceManager { protected static Session session; protected static TemporalPersistenceManager manager = null; protected static Logger log = LogManager.getLogger(TemporalPersistenceManager.class.getName()); public static Session getSession() { return session; } protected TemporalPersistenceManager() { } /** * Static function to get an instance. * * @param prop A properties file with the hibernate configuration. * @param annotatedClasses A list with the annotated persistence classes. * @return An instance of the persistence manager. */ public static TemporalPersistenceManager getInstance(Properties prop, List<Class> annotatedClasses) { if (manager == null) { session = TemporalHibernateUtil.getSession(prop, annotatedClasses); manager = new TemporalPersistenceManager(); } return manager; } /** * Determines if the given entity is a valid-time object or not. * * @param entity The entity to persist. * @return True if the entity is a valid-time object, false otherwise. */ protected PossibilisticVTP getValidTime(Object entity) throws IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<?> c = entity.getClass(); Field[] declaredFields = c.getDeclaredFields(); PossibilisticVTP validTime = null; boolean found = false; for (int i = 0; i < declaredFields.length && !found; i++) { if (declaredFields[i].getType().equals(PossibilisticVTP.class)) { found = true; // validTime = (PossibilisticVTP) declaredFields[i].get(entity); String fieldName = declaredFields[i].getName(); fieldName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // Class [] noparams = {}; Object invoke = entity.getClass().getMethod(fieldName, new Class[] {}).invoke(entity, null); validTime = (PossibilisticVTP) invoke; } } return validTime; } /** * Update the valid time value for a given entity * * @param entity The entity * @param vt The new valid time value. * @throws NoSuchMethodException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException */ protected void updateValidTime(Object entity, PossibilisticVTP vt) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class<?> c = entity.getClass(); Field[] declaredFields = c.getDeclaredFields(); PossibilisticVTP validTime = null; boolean found = false; for (int i = 0; i < declaredFields.length && !found; i++) { if (declaredFields[i].getType().equals(PossibilisticVTP.class)) { found = true; // validTime = (PossibilisticVTP) declaredFields[i].get(entity); String fieldName = declaredFields[i].getName(); fieldName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); // Class [] noparams = {}; Object invoke = entity.getClass().getMethod(fieldName, new Class[] { PossibilisticVTP.class }) .invoke(entity, vt); validTime = (PossibilisticVTP) invoke; } } } /** * Obtains the primary key of a given temporal object * * @param temporalEntity The entity * @return The primary key. * @throws TemporalException */ public TemporalPK getPK(Object temporalEntity) throws TemporalException { if (temporalEntity == null) { return null; } else { TemporalPK key; try { key = (TemporalPK) temporalEntity.getClass().getMethod("getTid", new Class[] {}) .invoke(temporalEntity, null); return key; } catch (Exception ex) { log.error(" Error obtaining the pk of a temporal object"); throw new TemporalException(ex); } } } /** * Persist the given transient temporal instance, assigning a generated * identifier. If the object is a valid-time object, then the persistence * algorithm is the following: <ul> <li>If the item does overlap with any * existing items, then reject the insertion. </li> <li>If the item does not * overlap any existing items, then insert.</li> <li>If exist any open * tuple, then call modify.</li> </ul> * * * * @param entity the object to persist. * @return The generated identifier. * @throws HibernateException In case of error. * @throws TemporalInsertException In case of error when inserting: The * interval does overlaps or during any other existing valid time object. * */ public Serializable save(Object entity) throws HibernateException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, TemporalInsertException, TemporalException { //retrieve whether the object is a valid-time entity or not: Serializable generatedId = null; PossibilisticVTP validTime = getValidTime(entity); if (validTime != null) { TemporalPK key = getPK(entity); // first of all, check if exists an open version: Criteria cmodify = session.createCriteria(entity.getClass()); cmodify = criteriaModify(cmodify, key); List updateList = cmodify.list(); if (updateList.size() > 0) { //close the current version Object oldEntity = updateList.get(0); updateEntity(entity, oldEntity, validTime); } else { // if no open tuples, then check if the insertion is possible. Criteria criteria = session.createCriteria(entity.getClass()); criteria = criteriaInsert(criteria, validTime, key); List list = criteria.list(); if (list.size() > 0) { // the list has some elements, and the insertion is rejected. log.debug("Insertion rejected"); throw new TemporalInsertException( "Insertion Rejected: The valid time overlaps or during any other valid time"); } else { log.debug("Insertion done"); generatedId = session.save(entity); } } // criteria.add(Restrictions.) //session. } else { generatedId = session.save(entity); } return generatedId; } /** * Function that stablishes the constrains for the temporal insertion. * * @param c An initialized criteria object. * @param validTime The valid time to intert * @return A criteria to query the database. */ protected Criteria criteriaInsert(Criteria c, PossibilisticVTP validTime, TemporalPK key) { if (c == null) { return null; } c = c.add(Restrictions.and(Restrictions.gt("pvp.startMP", validTime.getStartMP()), Restrictions.or( Restrictions.and(Restrictions.lt("pvp.startMP", validTime.getEndMP()), Restrictions.gt("pvp.endMP", validTime.getEndMP())), Restrictions.lt("pvp.endMP", validTime.getEndMP())), Restrictions.eq("tid.id", key.getId()))); return c; } /** * Function for release the connection. * * @return */ public Connection close() { return TemporalPersistenceManager.session.close(); } /** * Starts a new transaction * * @return */ public Transaction beginTransaction() { return session.beginTransaction(); } /** * Function to close a vtp from another * * @param toClose The vtp to close * @param newVtp The other vtp to start * @return A copy of the object toClose closed to the left. * @throws TemporalException If the closure of the vtp can not be computed. */ public PossibilisticVTP closeR(PossibilisticVTP toClose, PossibilisticVTP newVtp) throws TemporalException { if (toClose.getSide() != null && toClose.getSide().compareTo(OpenInterval.UC) == 0) { DateTime startmp = new DateTime(toClose.getStartMP()); // DateTime leftmp = startmp.minus(toClose.getStartLeft()); DateTime rightmp = startmp.plus(toClose.getStartRight()); DateTime newmp = new DateTime(newVtp.getStartMP()); DateTime newleft = newmp.minus(newVtp.getStartLeft()); // DateTime newright = newmp.plus(newVtp.getStartRight()); if (rightmp.isBefore(newleft)) { log.trace("Closing ending point"); Duration d = new Duration(startmp, newmp); Duration d1 = new Duration(d.getMillis() / 2); DateTime closeMp = new DateTime(startmp); closeMp = closeMp.plus(d1); Duration left = new Duration(startmp, closeMp); Duration right = new Duration(closeMp, newleft); toClose.setEndMP(closeMp.getMillis()); toClose.setEndLeft(left.getMillis()); toClose.setEndRight(right.getMillis()); toClose.setSide(null); } else { log.error("The point cannot be closed"); throw new TemporalException("The point cannot be closed"); } // // DateTime lefts = startmp.plus( new Instant(newVtp.getStartLeft())); // if(newVtp.getStartMP()> ) } else { log.error("The point is not open"); throw new TemporalException("The point is not open"); } return toClose; } /** * Update the given entity. If the entity is a temporal entity, then, the * last version of the same entity is closed and updated by this one. * * @param entity The entity to update * @throws HibernateException In case of any Hibernate error. * @throws TemporalException In case of an error in the temporal * calculations. */ public void update(Object entity) throws HibernateException, TemporalException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { PossibilisticVTP validTime = getValidTime(entity); if (validTime != null) { TemporalPK key = getPK(entity); // first: retrieve the more recent object with the same pk: Criteria c = session.createCriteria(entity.getClass()); c = criteriaModify(c, key); List list = c.list(); if (list.size() == 1) { Object oldEntity = list.get(0); updateEntity(entity, oldEntity, validTime); } else { log.debug("There are temporal inconsistencies in the database"); } } else { log.debug("updating non-temporal entity"); session.update(entity); } } /** * Core for the update entity * @param newEntity The new entity to update. * @param oldEntity The old entity. * @param newValidTime The new value for the valid-time. * @throws TemporalException In case of error */ protected void updateEntity(Object newEntity, Object oldEntity, PossibilisticVTP newValidTime) throws TemporalException { try { PossibilisticVTP toClose = getValidTime(oldEntity); //in case of error, the closeR function aborts the update: PossibilisticVTP closed = closeR(toClose, newValidTime); updateValidTime(oldEntity, closed); //update the old entity: session.update(oldEntity); // save the new entity: session.save(newEntity); } catch (Exception e) { log.error(e); throw new TemporalException(e); } } /** * Criteria for the insert operation * * @param criteria An initialized criteria * @param key the temporal primary key of the object * @return The criteria to modify a tuple. */ protected Criteria criteriaModify(Criteria criteria, TemporalPK key) { return criteria.add(Restrictions.and(Restrictions.eq("tid.id", key.getId()), Restrictions.eq("pvp.side", OpenInterval.UC))); } /** * Removes a persistent instance from the datastore * * @param entity The instance. If entity is a temporal instance, then, all * the versions are deleted. * @throws HibernateException In case of Hibernate Error * @throws TemporalException In case of temporal error. */ public void delete(Object entity) throws HibernateException, TemporalException { try { PossibilisticVTP validTime = getValidTime(entity); if (validTime != null) { TemporalPK key = getPK(entity); String tableName = entity.getClass().getSimpleName(); session.createQuery("Delete from " + tableName + " where tid.id = :idval") .setParameter("idval", key.getId()).executeUpdate(); } else { log.debug("delete of non-temporal object"); session.delete(entity); } } catch (Exception e) { throw new TemporalException(e); } } public TemporalQuery createTemporalQuery(Object entity) { return new TemporalQuery(session, entity); } }