/** * * @author yangzhichao * * @param <PK> * @param <T> */ public interface TreeNode<PK extends Number, T extends TreeNode<PK, T>> extends Serializable { public static final String DEFAULT_PATH_SEPARATOR = "-"; /** * 节点标识 * * @return */ PK getId(); /** * 节点名字 * * @return */ String getName(); /** * 获取直接父节点对象 * * @return 直接上级对象 */ T getParent(); /** * 设置父节点 * * @param t */ void setParent(T t); /** * 获取路径 * * @return */ String getPath(); /** * * @param path * @return */ void setPath(String path); /** * 是否为叶节点 * * @return 返回所有叶节点 */ boolean isLeaf(); /** * 判断是否为指定节点的父节点 * * @param child * 被判断的节点 * @return */ boolean isParentOf(T child); /** * 判断是否为指定节点的子节点 * * @param parent * 被判断的节点对象 * @return */ boolean isChildOf(T parent); /** * 节点是否有效 * * @return */ boolean isValid(); /** * 获取子节点 * * @return */ Collection<T> getChildren(); /** * 节点类别 * * @return */ @SuppressWarnings("unchecked") Class getClazz(); /** * 状态 * * @return */ int getStatus(); }
/** * 树型抽象实现 * * @author yangzhichao * */ public abstract class AbstractTreeNode<PK extends Number, T extends AbstractTreeNode<PK, T>> implements TreeNode<PK, T> { /** * 标识 */ private PK id; /** * 名字 */ private String name; /** * 上级节点 */ private T parent; /** * 路径 */ private String path; /** * 直接子节点集合 */ private Collection<T> children; /** * 序号 */ private int order; public boolean isChildOf(T parent) { return parent == null || this.getParent() == null ? false : this .getParent().equals(parent); } public boolean isParentOf(T child) { return child == null || child.getParent() == null ? false : this .equals(child.getParent()); } /** * 默认根据是否有子节点判断 */ public boolean isLeaf() { return !(this.getChildren() != null && this.getChildren().size() > 0); } @SuppressWarnings("unchecked") public boolean equals(Object object) { if (!(object instanceof AbstractTreeNode)) { return false; } AbstractTreeNode o = (AbstractTreeNode) object; // 类型和ID要一致 return new EqualsBuilder().append(this.getClazz().getName(), o.getClazz().getName()).append(this.getId(), o.getId()) .isEquals(); } public PK getId() { return id; } public void setId(PK id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public T getParent() { return parent; } public void setParent(T parent) { this.parent = parent; } public Collection<T> getChildren() { return children; } public void setChildren(Collection<T> children) { this.children = children; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } }
/** * 树型DAO接口 * * @author yangzhichao * * @param <PK> * @param <T> */ public interface TreeNodeDao<PK extends Number, T extends TreeNode<PK, T>> extends BaseDao<PK, T> { /** * 获取父亲节点 * * @param t * @return */ T getParent(T t) throws DataAccessException; /** * 获取直接子节点 * * @param t * @return * @throws DataAccessException */ Collection<T> getDirectChildren(T t) throws DataAccessException; /** * 获取直接子枝干 * * @param t * @return */ Collection<T> getDirectBranches(T t) throws DataAccessException; /** * 获取直接叶子节点 * * @param t * @return */ Collection<T> getDirectLeaves(T t) throws DataAccessException; /** * 获取所有孩子 * * @param t * @return * @throws DataAccessException */ Collection<T> getAllChildren(T t) throws DataAccessException; /** * 通过PATH获取所有子节点 * * @param t * @return * @throws DataAccessException */ Collection<T> getAllChildrenByPath(T t) throws DataAccessException; /** * 通过PATH获取所有子枝干 * * @param t * @return * @throws DataAccessException */ Collection<T> getAllBranchesByPath(T t) throws DataAccessException; /** * 通过PATH获取所有子叶子 * * @param t * @return * @throws DataAccessException */ Collection<T> getAllLeavesByPath(T t) throws DataAccessException; /** * 通过PATH获取所有父节点 * * @param t * @return */ Collection<T> getAllParentByPath(T t) throws DataAccessException; /** * 通过PATH获取到ROOT节点为止(包含ROOT)的所有父亲节点 * * @param t * @param root * @return */ Collection<T> getAllParentByPath(T t, T root) throws DataAccessException; /** * 更新所有path属性包含oldPath子串的节点的path属性的oldPath子串为newPath * * @param oldPath * @param newPath * @throws DataAccessException */ void updatePath(Class<T> clazz, String oldPath, String newPath) throws DataAccessException; }
/** * * @author yangzhichao * * @param <PK> * @param <T> */ public abstract class HibernateTreeNodeDao<PK extends Number, T extends TreeNode<PK, T>> extends SpringHibernateDao<PK, T> implements TreeNodeDao<PK, T> { /** * 根据ID字符串和类别获取对象 * * @param id * @param c * @return */ protected abstract T get(String id, Class<T> c); @SuppressWarnings("unchecked") public Collection<T> getAllParentByPath(T t) { ArrayList<T> parents = new ArrayList<T>(); for (String parentID : t.getPath().trim().split("-")) { if (parentID != null && !parentID.trim().equals("") && !parentID.trim().equals("-")) { parents.add(this.get(parentID, (Class<T>) t.getClazz())); } } return parents; } @SuppressWarnings("unchecked") public Collection<T> getAllParentByPath(T t, T root) { ArrayList<T> parents = new ArrayList<T>(); for (String parentID : t.getPath().trim().split("-")) { if (parentID != null && !parentID.trim().equals("") && !parentID.trim().equals("-")) { parents.add(this.get(parentID, (Class<T>) t.getClazz())); if (root.getId().toString().equals(parentID.trim())) { break; } } } return parents; } public T getParent(T t) { return t.getParent(); } /** * 路径分隔符 */ public String getPathSeparator() { return TreeNode.DEFAULT_PATH_SEPARATOR; } /** * 设置节点t的路径 * * @param t */ private void setPath(T t) { // 设置路径 if (t.getParent() == null) { t.setPath("-"); } else { t.setPath(t.getParent().getPath() + t.getParent().getId() + this.getPathSeparator()); } } /** * 插入 * * @throws */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = DataAccessException.class) public void insert(T t) throws DataAccessException { this.setPath(t); super.insert(t); } /** * 更新 * * @throws */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = DataAccessException.class) public void update(T t) throws DataAccessException { boolean parentChanged = false; T old = this.get(t.getId(), t.getClazz()); T oldParent = this.getParent(old); if (oldParent != null && !oldParent.equals(t.getParent()) || (oldParent == null && t.getParent() != null)) { // 上级节点改变 parentChanged = true; } // 清除旧副本,不然保存时候会找到两个同一ID的对象会报错 this.getHibernateTemplate().evict(old); if (parentChanged) { if (t.getParent() != null && t.getParent().getPath() != null && t.getParent().getPath().indexOf("-"+t.getId().toString()+"-") != -1) { throw new DataAccessException(this.getClass(), "不能选择子节点作为当前节点的父节点"); } if (t.getParent() != null && t.getParent().equals(t)) { throw new DataAccessException(this.getClass(), "不能选择本身作为当前节点的父节点"); } // 设置路径 this.setPath(t); // 重新设置所有子节点路径 this.updatePath(t.getClazz(), old.getPath(), t.getPath()); } super.update(t); } /** * 更新路径,当某节点上级节点改变时候调用 * * @throws DataAccessException */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED,rollbackFor = DataAccessException.class) public void updatePath(final Class<T> clazz, String oldPath, String newPath) throws DataAccessException { if (oldPath == null || oldPath.trim().length() < 1 || oldPath.trim().equals("-")) { return; } if (newPath == null || newPath.trim().length() < 1) { newPath = "-"; } if (oldPath.charAt(0) != '-') { oldPath = "-" + oldPath; } if (oldPath.charAt(oldPath.length() - 1) != '-') { oldPath = oldPath + "-"; } if (newPath.charAt(0) != '-') { newPath = "-" + newPath; } if (newPath.charAt(newPath.length() - 1) != '-') { newPath = newPath + "-"; } final String newPathTemp = newPath; final String oldPathTemp = oldPath; try { this.getHibernateTemplate().execute(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException, SQLException { StringBuilder hql = new StringBuilder(); hql.append(" update "); hql.append(clazz.getName()); hql.append(" o "); hql.append(" set o.path=replace(o.path,:oldPath"); hql.append(",:newPath"); hql.append(")"); hql.append(" where o.path like :oldPathLike "); Query query = session.createQuery(hql.toString()); query.setString("oldPath", oldPathTemp.trim()).setString( "newPath", newPathTemp.trim()).setString( "oldPathLike", "%" + oldPathTemp.trim() + "%") .executeUpdate(); return null; } }); } catch (Exception e1) { throw new DataAccessException(this.getClass(), "更新路径失败", e1); } } public Collection<T> getAllChildren(T t) throws DataAccessException { return this.getAllChildrenByPath(t); } public Collection<T> getAllChildrenByPath(T t) throws DataAccessException { HibernateCriteriaQuery query = new HibernateCriteriaQuery(); DetachedCriteria dc = DetachedCriteria.forClass(t.getClazz()); dc.add(Restrictions.like("path", "%-" + t.getId().toString() + "-%")); query.setQuery(dc); return this.find(query); } public Collection<T> getDirectChildren(T t) throws DataAccessException { HibernateCriteriaQuery query = new HibernateCriteriaQuery(); DetachedCriteria dc = DetachedCriteria.forClass(t.getClazz()); dc.add(Restrictions.eq("parent", t)); query.setQuery(dc); return this.find(query); } }