Java tutorial
/* Copyright (C) 2015, University of Kansas Center for Research * * Specify Software Project, specify@ku.edu, Biodiversity Institute, * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package edu.ku.brc.specify.treeutils; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Vector; import org.apache.log4j.Logger; import org.hibernate.Hibernate; import org.hibernate.LockMode; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import edu.ku.brc.af.core.db.DBTableIdMgr; import edu.ku.brc.af.core.db.DBTableInfo; import edu.ku.brc.af.core.expresssearch.QueryAdjusterForDomain; import edu.ku.brc.af.prefs.AppPreferences; import edu.ku.brc.af.ui.forms.BusinessRulesIFace; import edu.ku.brc.af.ui.forms.BusinessRulesIFace.STATUS; import edu.ku.brc.dbsupport.CustomQueryListener; import edu.ku.brc.dbsupport.DataProviderSessionIFace; import edu.ku.brc.dbsupport.HibernateUtil; import edu.ku.brc.dbsupport.DataProviderSessionIFace.QueryIFace; import edu.ku.brc.specify.datamodel.DataModelObjBase; import edu.ku.brc.specify.datamodel.TreeDefIface; import edu.ku.brc.specify.datamodel.TreeDefItemIface; import edu.ku.brc.specify.datamodel.Treeable; import edu.ku.brc.specify.datamodel.busrules.BaseTreeBusRules; import edu.ku.brc.specify.dbsupport.HibernateDataProviderSession; import edu.ku.brc.specify.ui.treetables.TreeNode; import edu.ku.brc.ui.UIRegistry; import edu.ku.brc.util.Pair; /** * An implementation of @see {@link TreeDataService} that uses Hibernate * capabilities. * * @code_status Beta * @author jstewart */ public class HibernateTreeDataServiceImpl<T extends Treeable<T, D, I>, D extends TreeDefIface<T, D, I>, I extends TreeDefItemIface<T, D, I>> implements TreeDataService<T, D, I> { /** * A <code>Logger</code> object used for all log messages eliminating from * this class. */ protected static final Logger log = Logger.getLogger(HibernateTreeDataServiceImpl.class); /** An {@link Interceptor} that logs all objects loaded by Hibernate. */ //public static HibernateLoadLogger loadLogger = new HibernateLoadLogger(); private boolean showTaxonAuthor = false; /** * Constructor. */ public HibernateTreeDataServiceImpl() { super(); showTaxonAuthor = AppPreferences.getRemote().getBoolean("TaxonTreeEditor.DisplayAuthor", false); } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#findByName(edu.ku.brc.specify.datamodel.TreeDefIface, java.lang.String) */ @SuppressWarnings("unchecked") public synchronized List<T> findByName(final D treeDef, final String name, final boolean isExact) { Vector<T> results = new Vector<T>(); Class<T> nodeClass = treeDef.getNodeClass(); Session session = getNewSession(treeDef); try { DBTableInfo tableInfo = DBTableIdMgr.getInstance().getByClassName(nodeClass.getName()); String columns = QueryAdjusterForDomain.getInstance().getSpecialColumns(tableInfo, true); String sql = "FROM " + nodeClass.getSimpleName() + " as node WHERE " + columns + " AND node.name LIKE :name"; Query q = session.createQuery(sql); String newName = name; if (!isExact) { if (newName.contains("*")) ; { newName = newName.replace("*", "%"); } if (!newName.endsWith("%")) { newName += "%"; } } q.setParameter("name", newName); for (Object o : q.list()) { T t = (T) o; // force loading on all ancestors T parent = t.getParent(); while (parent != null) { parent = parent.getParent(); } results.add(t); } Collections.sort(results, new TreePathComparator<T, D, I>(true)); } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return results; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getChildNodes(edu.ku.brc.specify.datamodel.Treeable) */ public synchronized Set<T> getChildNodes(final T parent) { if (Hibernate.isInitialized(parent.getChildren())) { return parent.getChildren(); } Set<T> children = null; Session session = getNewSession(parent); try { children = parent.getChildren(); // to force Set loading //int childCount = children.size(); //log.debug("getChildNodes( " + parent + " ): " + childCount + " child(ren) of " + parent.getName() + " loaded"); for (@SuppressWarnings("unused") T child : children) { //log.debug("\t" + nodeDebugInfo(child)); } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return children; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getChildTreeNodes(edu.ku.brc.specify.datamodel.Treeable) */ @SuppressWarnings("unchecked") public List<TreeNode> getChildTreeNodes(final T parent) { Vector<TreeNode> treeNodes = new Vector<TreeNode>(); if (parent != null) { Session session = getNewSession(parent); try { String childQueryString = TreeFactory.getChildQueryString(parent); Query getNodeInfoList = session.createQuery(childQueryString); getNodeInfoList.setParameter("PARENT", parent); List list = getNodeInfoList.list(); List<Object[]> nodeInfoList = list; for (Object[] nodeInfo : nodeInfoList) { treeNodes.add(createNode(nodeInfo, parent)); } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } } return treeNodes; } /** * Creates a {@link TreeNode} from the passed in {@link Object} array. The array * must contain the following objects, in order: * <ol start="0"> * <li>{@link Integer} id * <li>{@link String} name * <li>{@link String} fullName * <li>{@link Integer} nodeNumber * <li>{@link Integer} highestChildNodeNumber * <li>{@link Integer} rankId * <li>{@link Integer} acceptedParent.rankId * <li>{@link String} acceptedParent.fullName * </ol> * * (The acceptedParent fields will commonly be <code>null</code>.) * * @param nodeInfo an object array containing the node info * @param parent the parent record * @return a {@link TreeNode} object */ private TreeNode createNode(final Object[] nodeInfo, final T parent) { Integer id = (Integer) nodeInfo[0]; String nodeName = (String) nodeInfo[1]; String fullName = (String) nodeInfo[2]; Integer nodeNum = (Integer) nodeInfo[3]; Integer highChild = (Integer) nodeInfo[4]; int rank = (Integer) nodeInfo[5]; Integer acceptedParentId = (Integer) nodeInfo[6]; String acceptedParentFullName = (String) nodeInfo[7]; if (showTaxonAuthor && nodeInfo.length > 8) { String auth = (String) nodeInfo[8]; if (auth != null) { nodeName += " " + auth; } } int descCount = 0; if (highChild != null && nodeNum != null) { descCount = highChild - nodeNum; } int parentId; int parentRank; T parentRecord = parent; if (parentRecord == null) { parentId = id; parentRank = -1; } else { parentId = parentRecord.getTreeId(); parentRank = parentRecord.getRankId(); } Set<Pair<Integer, String>> synIdsAndNames = getSynonymIdsAndNames(parent.getClass(), id); TreeNode node = new TreeNode(nodeName, fullName, id, parentId, rank, parentRank, (descCount != 0), acceptedParentId, acceptedParentFullName, synIdsAndNames); if (parent != null) { node.setDataObjClass(parent.getClass()); } return node; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getRelatedRecordCount(java.lang.Class, int, edu.ku.brc.dbsupport.CustomQueryListener) */ public void calcRelatedRecordCount(final Class<?> clazz, final int id, final CustomQueryListener listener) { //String queryStr = TreeFactory.getRelatedRecordCountQueryString(clazz, id); //JPAQuery jpaQuery = new JPAQuery(queryStr, listener); //jpaQuery.start(); } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getRootNode(edu.ku.brc.specify.datamodel.TreeDefIface) */ public synchronized T getRootNode(final D treeDef) { T root = null; Session session = getNewSession(treeDef); try { for (I defItem : treeDef.getTreeDefItems()) { if (defItem.getParent() == null) { Iterator<T> entries = defItem.getTreeEntries().iterator(); if (entries.hasNext()) { root = defItem.getTreeEntries().iterator().next(); break; } } } if (root != null) { //log.debug("Root node: " + nodeDebugInfo(root)); } else { //log.debug("No root node"); } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return root; } @SuppressWarnings("unchecked") public synchronized T getNodeById(final Class<?> clazz, final int id) { //log.debug("getNodeById( " + clazz.getSimpleName() + ", " + id + " )"); Session session = getNewSession(); try { return (T) session.load(clazz, id); } catch (org.hibernate.ObjectNotFoundException e) { log.error(e); } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return null; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getAllTreeDefs(java.lang.Class) */ @SuppressWarnings("unchecked") public synchronized List<D> getAllTreeDefs(Class<D> treeDefClass) { Vector<D> defs = null; Session session = getNewSession(); try { Query q = session.createQuery("FROM " + treeDefClass.getSimpleName()); List<?> results = q.list(); defs = new Vector<D>(results.size()); for (Object o : results) { D def = (D) o; // force loading of all related def items def.getTreeDefItems().size(); defs.add(def); } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return defs; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getTreeDef(java.lang.Class, int) */ @SuppressWarnings("unchecked") public synchronized D getTreeDef(Class<D> defClass, int defId) { Session session = getNewSession(); try { String className = defClass.getSimpleName(); String idFieldName = className.toLowerCase().substring(0, 1) + className.substring(1) + "Id"; Query query = session.createQuery("FROM " + className + " WHERE " + idFieldName + "=:defId"); query.setParameter("defId", defId); D def = (D) query.uniqueResult(); // force loading of all related def items // (they should load anyway as long as FetchType.EAGER is set on getTreeDefItems()) def.getTreeDefItems().size(); return def; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return null; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getDescendantCount(edu.ku.brc.specify.datamodel.Treeable) */ public synchronized int getDescendantCount(final T node) { if (node == null) { return 0; } Session session = getNewSession(node); //log.debug("refreshing " + nodeDebugInfo(node)); try { //session.refresh(node); Integer nodeNum = node.getNodeNumber(); Integer highChild = node.getHighestChildNodeNumber(); int descCnt = 0; if (nodeNum != null && highChild != null) { descCnt = highChild - nodeNum; } else { descCnt = node.getDescendantCount(); } return descCnt; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return 0; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#deleteTreeNode(edu.ku.brc.specify.datamodel.Treeable) */ @SuppressWarnings("unchecked") public synchronized boolean deleteTreeNode(final T nodeToDelete) { //Session session = getNewSession(node); Session session = HibernateUtil.getSessionFactory().openSession(); try { T node = (T) mergeIntoSession(session, nodeToDelete); // refresh the node data so we have correct information for the following calculation session.refresh(node); T parent = node.getParent(); if (parent != null) { parent = (T) mergeIntoSession(session, parent); session.refresh(parent); } Transaction tx = session.beginTransaction(); // detach from the parent node if (parent != null) { parent.removeChild(node); node.setParent(null); } // let Hibernate delete the subtree DataProviderSessionIFace sessionWrapper = new HibernateDataProviderSession(session); BusinessRulesIFace busRulesObj = DBTableIdMgr.getInstance().getBusinessRule(node); if (busRulesObj != null) { node = (T) busRulesObj.beforeDelete(node, sessionWrapper); } session.delete(node); if (busRulesObj != null) { try { if (!busRulesObj.beforeDeleteCommit(node, sessionWrapper)) { tx.rollback(); return false; } } catch (Exception e) { tx.rollback(); return false; } } boolean retVal = commitTransaction(session, tx); // NOTE: this closes an open session if (busRulesObj != null && retVal) { busRulesObj.afterDeleteCommit(node); } return retVal; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { if (session.isOpen()) { session.close(); } } return false; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#nodesSkippingOverLevel(int, edu.ku.brc.specify.datamodel.TreeDefIface) */ public List<String> nodesSkippingOverLevel(final int levelSkippedRank, final D treeDef) { Session session = getNewSession(); try { Class<T> nodeClass = treeDef.getNodeClass(); Query nodeSkippingLevelQuery = session.createQuery("select n.fullName from " + nodeClass.getName() + " n where rankId>:rankID AND parent.rankId<:rankID AND definition=:treeDef"); nodeSkippingLevelQuery.setParameter("rankID", levelSkippedRank); // nodeSkippingLevelQuery.setParameter("rankID2", levelSkippedRank); nodeSkippingLevelQuery.setParameter("treeDef", treeDef); List<?> results = nodeSkippingLevelQuery.list(); Vector<String> nodeNames = new Vector<String>(results.size()); for (Object o : results) { nodeNames.add((String) o); } return nodeNames; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return null; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#nodeNamesAtLevel(int, edu.ku.brc.specify.datamodel.TreeDefIface) */ public List<String> nodeNamesAtLevel(final int rankID, final D treeDef) { Session session = getNewSession(); try { Class<T> nodeClass = treeDef.getNodeClass(); Query nodeNamesQuery = session.createQuery("select n.fullName from " + nodeClass.getName() + " n where rankId=:rankID AND definition=:treeDef"); nodeNamesQuery.setParameter("rankID", rankID); nodeNamesQuery.setParameter("treeDef", treeDef); List<?> results = nodeNamesQuery.list(); Vector<String> nodeNames = new Vector<String>(results.size()); for (Object o : results) { nodeNames.add((String) o); } return nodeNames; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return null; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#countNodesAtLevel(int, edu.ku.brc.specify.datamodel.TreeDefIface) */ public int countNodesAtLevel(final int rankID, final D treeDef) { Session session = getNewSession(); try { Class<T> nodeClass = treeDef.getNodeClass(); Query nodeCountQuery = session.createQuery("select count(n) from " + nodeClass.getName() + " n where rankID=:rankID AND definition=:treeDef"); nodeCountQuery.setParameter("rankID", rankID); nodeCountQuery.setParameter("treeDef", treeDef); Integer count = (Integer) nodeCountQuery.uniqueResult(); return count; } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return 0; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#updateNodeNumbersAfterNodeEdit(edu.ku.brc.specify.datamodel.Treeable, edu.ku.brc.dbsupport.DataProviderSessionIFace) */ public synchronized boolean updateNodeNumbersAfterNodeEdit(final T node, final DataProviderSessionIFace session) throws Exception { if (BaseTreeBusRules.ALLOW_CONCURRENT_FORM_ACCESS) { /*XXX. * (this seems to work, but node has actually already been saved...apparently the commit or * a merge saves these changes... * * Should be moved to a point before the save occurs. */ //basically just makes sure NodeNumbers are refreshed from the db QueryIFace q = session.createQuery("select nodeNumber, highestChildNodeNumber from " + node.getClass().getSimpleName().toLowerCase() + " where " + node.getClass().getSimpleName() + "ID = " + node.getTreeId() + " and " + node.getClass().getSimpleName() + "TreeDefID = " + node.getDefinition().getTreeDefId(), true); Object resObj = q.uniqueResult(); Object[] result = (Object[]) resObj; node.setNodeNumber((Integer) result[0]); node.setHighestChildNodeNumber((Integer) result[1]); } return true; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#updateNodeNumbersAfterNodeAddition(edu.ku.brc.specify.datamodel.Treeable, edu.ku.brc.dbsupport.DataProviderSessionIFace) */ public synchronized boolean updateNodeNumbersAfterNodeAddition(final T newNode, final DataProviderSessionIFace session) throws Exception { // update the nodeNumber and highestChildNodeNumber fields for all effected nodes boolean doNodeNumberUpdate = true; T parent = newNode.getParent(); Integer parentNN = null; if (parent == null) { doNodeNumberUpdate = false; } else { parentNN = parent.getNodeNumber(); if (parentNN == null) { doNodeNumberUpdate = false; } } if (!doNodeNumberUpdate) { return true; } // else, node number update needed T mergedParent = session.merge(parent); session.refresh(mergedParent); parentNN = mergedParent.getNodeNumber(); String className = mergedParent.getClass().getName(); TreeDefIface<T, D, I> def = mergedParent.getDefinition(); //Now update the node numbers. //This assumes that the if BaseTreeBusRules.ALLOW_CONCURRENT_FORM_ACCESS is true, //then caller has locked the "TreeFormSave" semaphore for the tree. String updateNodeNumbersQueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber+1 WHERE nodeNumber>:parentNN AND definition=:def"; QueryIFace fixNodeNumQuery = session.createQuery(updateNodeNumbersQueryStr, false); fixNodeNumQuery.setParameter("parentNN", parentNN); fixNodeNumQuery.setParameter("def", def); fixNodeNumQuery.executeUpdate(); // session.clear(); String updateHighChildQueryStr = "UPDATE " + className + " SET highestChildNodeNumber=highestChildNodeNumber+1 WHERE highestChildNodeNumber>=:parentNN AND definition=:def"; QueryIFace fixHighChildQuery = session.createQuery(updateHighChildQueryStr, false); fixHighChildQuery.setParameter("parentNN", parentNN); fixHighChildQuery.setParameter("def", def); fixHighChildQuery.executeUpdate(); // session.clear(); // now set the initial values of the nodeNumber and // highestChildNodeNumber fields for the new node int newChildNN = parentNN + 1; /*Begin flakey code block... * (this seems to work, but newNode has actually already been saved...apparently the commit or * a merge saves these changes... *newNode.setNodeNumber(newChildNN); *newNode.setHighestChildNodeNumber(newChildNN); *...end flakey code block */ //use queries instead of flakey code String setChildNNQueryStr = "UPDATE " + className + " SET nodeNumber=:newChildNN WHERE nodeNumber IS NULL AND parentID=:parentID"; QueryIFace setChildNNQuery = session.createQuery(setChildNNQueryStr, false); setChildNNQuery.setParameter("newChildNN", newChildNN); setChildNNQuery.setParameter("parentID", parent.getTreeId()); setChildNNQuery.executeUpdate(); // session.clear(); String setChildHCQueryStr = "UPDATE " + className + " SET highestChildNodeNumber=:newChildNN WHERE highestChildNodeNumber IS NULL AND parentID=:parentID"; QueryIFace setChildHCQuery = session.createQuery(setChildHCQueryStr, false); setChildHCQuery.setParameter("newChildNN", newChildNN); setChildHCQuery.setParameter("parentID", parent.getTreeId()); setChildHCQuery.executeUpdate(); // session.clear(); return true; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#updateNodeNumbersAfterNodeDeletion(edu.ku.brc.specify.datamodel.Treeable, edu.ku.brc.dbsupport.DataProviderSessionIFace) */ public boolean updateNodeNumbersAfterNodeDeletion(final T deletedNode, final DataProviderSessionIFace session) throws Exception { boolean success = true; // save the original nodeNumber and highestChildNodeNumber values Integer delNodeNN = deletedNode.getNodeNumber(); Integer delNodeHC = deletedNode.getHighestChildNodeNumber(); // update the nodeNumber and highestChildNodeNumber fields for all effected nodes boolean doNodeNumberUpdate = true; if (delNodeNN == null || delNodeHC == null) { doNodeNumberUpdate = false; } if (doNodeNumberUpdate) { int nodesDeleted = delNodeHC - delNodeNN + 1; String className = deletedNode.getClass().getName(); TreeDefIface<T, D, I> def = deletedNode.getDefinition(); // rods - 07/28/08 // Can't figure out why this never needed beginTranaction/commit // before now, unless reworking some of Josh's code removed this call form the middle // of another transaction. session.beginTransaction(); String updateNodeNumbersQueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber-:nodesDeleted WHERE nodeNumber>=:delNodeNN AND definition=:def"; QueryIFace fixNodeNumQuery = session.createQuery(updateNodeNumbersQueryStr, false); fixNodeNumQuery.setParameter("nodesDeleted", nodesDeleted); fixNodeNumQuery.setParameter("delNodeNN", delNodeNN); fixNodeNumQuery.setParameter("def", def); fixNodeNumQuery.executeUpdate(); //session.clear(); String updateHighChildQueryStr = "UPDATE " + className + " SET highestChildNodeNumber=highestChildNodeNumber-:nodesDeleted WHERE highestChildNodeNumber>=:delNodeHC AND definition=:def"; QueryIFace fixHighChildQuery = session.createQuery(updateHighChildQueryStr, false); fixHighChildQuery.setParameter("nodesDeleted", nodesDeleted); fixHighChildQuery.setParameter("delNodeHC", delNodeHC); fixHighChildQuery.setParameter("def", def); fixHighChildQuery.executeUpdate(); //session.clear(); session.commit(); } return success; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#moveTreeNode(edu.ku.brc.specify.datamodel.Treeable, edu.ku.brc.specify.datamodel.Treeable) */ @SuppressWarnings("unchecked") public synchronized int moveTreeNode(final T node, final T newParent) { //log.debug("Moving ["+nodeDebugInfo(node)+"] to ["+nodeDebugInfo(newParent)+"]"); if (node == null || newParent == null) { throw new NullPointerException("'node' and 'newParent' must both be non-null"); } if (node.getParent() == newParent) { return ERROR; } T oldParent = node.getParent(); if (oldParent == null) { throw new NullPointerException("'node' must already have a parent"); } BusinessRulesIFace busRules = DBTableIdMgr.getInstance().getBusinessRule(node); STATUS status = ((BaseTreeBusRules) busRules).checkForSiblingWithSameName(newParent, node, true); if (status != STATUS.OK) { return CANCELLED; } Session session = getNewSession(); try { T mergedNode = (T) mergeIntoSession(session, node); T mergedNewParent = (T) mergeIntoSession(session, newParent); T mergedOldParent = (T) mergeIntoSession(session, oldParent); Transaction tx = session.beginTransaction(); //log.debug("refreshing " + nodeDebugInfo(mergedNode)); session.refresh(mergedNode); //log.debug("refreshing " + nodeDebugInfo(mergedNewParent)); session.refresh(mergedNewParent); // fix up the parent/child pointers for the effected nodes // oldParent cannot be null at this point mergedOldParent.removeChild(mergedNode); mergedNewParent.addChild(mergedNode); mergedNode.setParent(mergedNewParent); //BusinessRulesIFace busRules = DBTableIdMgr.getInstance().getBusinessRule(mergedNode); HibernateDataProviderSession sessionWrapper = new HibernateDataProviderSession(session); if (busRules != null) { busRules.beforeSave(mergedNode, sessionWrapper); } session.saveOrUpdate(mergedNode); // fix all the node numbers for effected nodes // X will represent moving subtree's root node // Y will represent new parent of moving subtree // get the root node T rootNode = mergedNewParent; while (rootNode.getParent() != null) { rootNode = rootNode.getParent(); } int rootHC = rootNode.getHighestChildNodeNumber(); int xNN = mergedNode.getNodeNumber(); int xHC = mergedNode.getHighestChildNodeNumber(); int yNN = mergedNewParent.getNodeNumber(); D def = mergedNode.getDefinition(); String className = mergedNode.getClass().getName(); int numMoving = xHC - xNN + 1; // the HQL update statements that need to happen now are dependant on the 'direction' of the move boolean downwardMove = true; if (xNN > yNN) { downwardMove = false; } if (downwardMove) { // change node numbers for the moving nodes to high values in order to temporarily 'move them out of the tree' String step1QueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber+:rootHC, highestChildNodeNumber=highestChildNodeNumber+:rootHC WHERE nodeNumber>=:xNN AND nodeNumber<=:xHC AND definition=:def"; Query step1Query = session.createQuery(step1QueryStr); step1Query.setParameter("def", def); step1Query.setParameter("xNN", xNN); step1Query.setParameter("xHC", xHC); step1Query.setParameter("rootHC", rootHC); step1Query.executeUpdate(); String step2QueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber-:numMoving WHERE nodeNumber>:xHC AND nodeNumber<=:yNN AND definition=:def"; Query step2Query = session.createQuery(step2QueryStr); step2Query.setParameter("def", def); step2Query.setParameter("xHC", xHC); step2Query.setParameter("yNN", yNN); step2Query.setParameter("numMoving", numMoving); step2Query.executeUpdate(); String step3QueryStr = "UPDATE " + className + " SET highestChildNodeNumber=highestChildNodeNumber-:numMoving WHERE highestChildNodeNumber>=:xHC AND highestChildNodeNumber<:yNN AND definition=:def"; Query step3Query = session.createQuery(step3QueryStr); step3Query.setParameter("def", def); step3Query.setParameter("xHC", xHC); step3Query.setParameter("yNN", yNN); step3Query.setParameter("numMoving", numMoving); step3Query.executeUpdate(); String step4QueryStr = "UPDATE " + className + " SET highestChildNodeNumber=highestChildNodeNumber-nodeNumber WHERE nodeNumber>:rootHC AND definition=:def"; Query step4Query = session.createQuery(step4QueryStr); step4Query.setParameter("def", def); step4Query.setParameter("rootHC", rootHC); step4Query.executeUpdate(); String step5QueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber + :yNN - :xHC - :rootHC WHERE nodeNumber>:rootHC AND definition=:def"; Query step5Query = session.createQuery(step5QueryStr); step5Query.setParameter("def", def); step5Query.setParameter("xHC", xHC); step5Query.setParameter("yNN", yNN); step5Query.setParameter("rootHC", rootHC); step5Query.executeUpdate(); String step6QueryStr = "UPDATE " + className + " SET highestChildNodeNumber=nodeNumber+highestChildNodeNumber WHERE nodeNumber >:lowerBound AND nodeNumber<=:yNN AND definition=:def"; Query step6Query = session.createQuery(step6QueryStr); step6Query.setParameter("def", def); step6Query.setParameter("yNN", yNN); //step6Query.setParameter("numMoving", numMoving); step6Query.setParameter("lowerBound", yNN - numMoving); step6Query.executeUpdate(); } else { // change node numbers for the moving nodes to high values in order to temporarily 'move them out of the tree' String step1QueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber+:rootHC, highestChildNodeNumber=highestChildNodeNumber+:rootHC WHERE nodeNumber>=:xNN AND nodeNumber<=:xHC AND definition=:def"; Query step1Query = session.createQuery(step1QueryStr); step1Query.setParameter("def", def); step1Query.setParameter("xNN", xNN); step1Query.setParameter("xHC", xHC); step1Query.setParameter("rootHC", rootHC); step1Query.executeUpdate(); String step2QueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber+:numMoving WHERE nodeNumber>:yNN AND nodeNumber<:xNN AND definition=:def"; Query step2Query = session.createQuery(step2QueryStr); step2Query.setParameter("def", def); step2Query.setParameter("xNN", xNN); step2Query.setParameter("yNN", yNN); step2Query.setParameter("numMoving", numMoving); step2Query.executeUpdate(); String step3QueryStr = "UPDATE " + className + " SET highestChildNodeNumber=highestChildNodeNumber+:numMoving WHERE highestChildNodeNumber>=:yNN AND highestChildNodeNumber<:xHC AND definition=:def"; Query step3Query = session.createQuery(step3QueryStr); step3Query.setParameter("def", def); step3Query.setParameter("yNN", yNN); step3Query.setParameter("xHC", xHC); step3Query.setParameter("numMoving", numMoving); step3Query.executeUpdate(); String step4QueryStr = "UPDATE " + className + " SET highestChildNodeNumber=highestChildNodeNumber-nodeNumber WHERE nodeNumber>:rootHC AND definition=:def"; Query step4Query = session.createQuery(step4QueryStr); step4Query.setParameter("def", def); step4Query.setParameter("rootHC", rootHC); step4Query.executeUpdate(); String step5QueryStr = "UPDATE " + className + " SET nodeNumber=nodeNumber+1+:yNN-:xNN-:rootHC WHERE nodeNumber>:rootHC AND definition=:def"; Query step5Query = session.createQuery(step5QueryStr); step5Query.setParameter("def", def); step5Query.setParameter("xNN", xNN); step5Query.setParameter("yNN", yNN); step5Query.setParameter("rootHC", rootHC); step5Query.executeUpdate(); String step6QueryStr = "UPDATE " + className + " SET highestChildNodeNumber=highestChildNodeNumber+nodeNumber WHERE nodeNumber>:yNN AND nodeNumber<=:upperBound AND definition=:def"; Query step6Query = session.createQuery(step6QueryStr); step6Query.setParameter("def", def); step6Query.setParameter("yNN", yNN); //step6Query.setParameter("numMoving", numMoving); step6Query.setParameter("upperBound", yNN + numMoving); step6Query.executeUpdate(); } if (busRules != null) { try { boolean retVal = busRules.beforeSaveCommit(mergedNode, sessionWrapper); if (retVal == false) { tx.rollback(); return ERROR; } } catch (Exception e) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, e); tx.rollback(); return ERROR; } } boolean success = commitTransaction(session, tx); // NOTE: Closes open session if (busRules != null) { success &= busRules.afterSaveCommit(mergedNode, null); } if (success) { return SUCCESS; } else { return ERROR; } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { if (session.isOpen()) { session.close(); } } return ERROR; } /** * @param session * @param source * @param dest * @return */ protected boolean fixAdditionalRelationsips(Session session, T source, T dest) { if (source != null) { TreeAdditionalProcFactory.TreeAdditionalProcessing proc = TreeAdditionalProcFactory.getInstance() .createProcessor(source.getClass()); if (proc != null) { return proc.process(session, source, dest); } } return true; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#synonymize(edu.ku.brc.specify.datamodel.Treeable, edu.ku.brc.specify.datamodel.Treeable) */ /* * null return value indicates success. */ @Override @SuppressWarnings("unchecked") public String synonymize(final T source, final T destination) { String statusMsg = null; Session session = getNewSession(source); Transaction tx = null; try { T mergedDest = (T) mergeIntoSession(session, destination); T mergedSrc = (T) mergeIntoSession(session, source); tx = session.beginTransaction(); if (fixAdditionalRelationsips(session, mergedSrc, mergedDest)) { TreeHelper.createNodeRelationship(mergedSrc, mergedDest); if (!commitTransaction(session, tx)) // NOTE: this call will close an open session. { statusMsg = UIRegistry .getResourceString("HibernateTreeDataService.NodeSynonymizedAddlRelError"); } } else { if (tx != null) { tx.rollback(); } } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { if (session != null && session.isOpen()) { session.close(); } } return statusMsg; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#unSynonymize(edu.ku.brc.specify.datamodel.Treeable) */ @Override @SuppressWarnings("unchecked") public String unSynonymize(T node) { String statusMsg = null; Session session = getNewSession(node); try { T mergedNode = (T) mergeIntoSession(session, node); Transaction tx = session.beginTransaction(); if (fixAdditionalRelationsips(session, mergedNode, null)) { mergedNode.setIsAccepted(true); mergedNode.setAcceptedParent(null); if (!commitTransaction(session, tx)) // NOTE: this call will close an open session. { statusMsg = UIRegistry .getResourceString("HibernateTreeDataService.NodeUnSynonymizedAddlRelError"); } else { statusMsg = String.format( UIRegistry.getResourceString("HibernateTreeDataService.NodeUnSynonymized"), node.getFullName()); } } else { // Error Dialog } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { if (session != null && session.isOpen()) { session.close(); } } return statusMsg; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getSynonyms(edu.ku.brc.specify.datamodel.Treeable) */ public Set<T> getSynonyms(final T node) { Set<T> synonyms = null; Session session = getNewSession(node); try { synonyms = node.getAcceptedChildren(); synonyms.size(); } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return synonyms; } /** * @param clazz * @param id * @return */ public Set<T> getSynonyms(final Class<? extends Treeable<?, ?, ?>> clazz, final Integer id) { T node = getNodeById(clazz, id); return getSynonyms(node); } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#getSynonymIdsAndNames(java.lang.Class, java.lang.Integer) */ @SuppressWarnings("unchecked") public Set<Pair<Integer, String>> getSynonymIdsAndNames(final Class<?> clazz, final Integer nodeId) { Set<Pair<Integer, String>> idsAndNames = new HashSet<Pair<Integer, String>>(); Session session = getNewSession(); try { String queryString = TreeFactory.getSynonymQueryString(clazz); Query q = session.createQuery(queryString); q.setParameter("NODEID", nodeId); List<Object[]> results = q.list(); for (Object[] idAndName : results) { Integer id = (Integer) idAndName[0]; String name = (String) idAndName[1]; idsAndNames.add(new Pair<Integer, String>(id, name)); } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } return idsAndNames; } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#initializeRelatedObjects(edu.ku.brc.specify.datamodel.Treeable) */ public synchronized void initializeRelatedObjects(final T node) { Session session = null; try { session = getNewSession(node); if (session != null) { TreeHelper.initializeRelatedObjects(node); } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { if (session != null) { session.close(); } } } /* (non-Javadoc) * @see edu.ku.brc.specify.treeutils.TreeDataService#refresh(java.lang.Object[]) */ public synchronized void refresh(final Object... objects) { Session session = getNewSession(objects); try { for (Object o : objects) { session.refresh(o); } } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); log.error(ex); } finally { session.close(); } } /** * Creates a new Hibernate session and associates the given objects with it. * * @param objects the objects to associate with the new session * @return the newly created session */ private Session getNewSession(final Object... objects) { Session session = HibernateUtil.getSessionFactory().openSession(); for (Object o : objects) { if (o != null) { // make sure not to attempt locking an unsaved object DataModelObjBase dmob = (DataModelObjBase) o; if (dmob.getId() != null) { session.lock(o, LockMode.NONE); } } } return session; } /** * Merges object with session. * @param session the session * @param object the object to be merged * @return a new object after the merge. */ private Object mergeIntoSession(final Session session, final Object object) { return session.merge(object); } /** * Commits the given Hibernate transaction on the given session. A rollback * is performed, and false is returned, on failure. * * @param session the session * @param tx the transaction * @return true on success */ private boolean commitTransaction(final Session session, final Transaction tx) { boolean result = true; try { tx.commit(); } catch (Exception ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(HibernateTreeDataServiceImpl.class, ex); result = false; log.error("Error while committing transaction to DB", ex); tx.rollback(); } finally { if (session.isOpen()) { session.close(); } } return result; } /** * Returns a string representation of the given Object. If the object does not * implement {@link Treeable}, then {@link Object#toString()} is called. If the object * does implement {@link Treeable}, the node ID, name, and hashcode are used to create * a string representation. This method is for debugging purposes only. * * @param o any object * @return a string representation of the node */ @SuppressWarnings("unused") private String nodeDebugInfo(final Object o) { if (o instanceof Treeable) { Treeable<?, ?, ?> t = (Treeable<?, ?, ?>) o; return t.getTreeId() + " " + t.getName() + " 0x" + Integer.toHexString(t.hashCode()); } return o.toString(); } // /** // * This class is an extension of {@link EmptyInterceptor} that logs all // * objects loaded by Hibernate. This class is only intended for use in // * debugging. // * // * @author jstewart // * @code_status Complete. // */ // public static class HibernateLoadLogger extends EmptyInterceptor // { // @Override // public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) // { // String className = entity.getClass().getSimpleName(); // //log.debug("loaded " + className + " (" + id + ") at 0x" + entity.hashCode()); // return false; // } // } }