/**
*
* @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);
}
}
分享到:
相关推荐
这个表是树结构的基础,每个节点对应表中的一条记录。 2. **构建连接**:在节点表中,通过父节点ID字段关联不同节点,形成父子关系。这使得我们能够通过查询来构建树形结构。 3. **遍历与构建**:为了生成实际的树...
例如,它可以将每个节点的路径作为字符串返回,便于理解和分析树结构。 在实际应用中,例如组织结构的展示,我们可以创建一个包含部门信息的表`SYS_DEPT`,其中`dept_id`为主键,`par_dept_id`表示父级部门ID,以此...
树形结构数据库设计模仿了自然界中的树状模型,每个节点代表一个数据项,可以有零个或多个子节点,而只有一个父节点(根节点除外)。这种结构非常适合表示层级关系,如组织架构、目录结构、产品分类等。在数据库中,...
为了更高效地处理无限级分类,可以使用预计算(pre-calculated)的`path`字段,存储每个分类从根到当前节点的路径。例如,对于`id=4`且`parent_id=2`的分类,`path`可能为`1/2/4`。这样,我们可以快速定位到任意分类...
通过递归的方式实现了树形结构的深度优先遍历,适用于多种场景下的数据处理需求。此外,还涉及到了环境配置、类文件加载、配置文件读取等实际开发中的常见问题。希望本文能帮助读者更好地理解和应用树形结构的遍历...
- 树形结构通常由嵌套的HTML无序列表(`<ul>`和`<li>`)实现,通过递归函数或循环遍历数据库记录来生成层次结构。 - 使用JavaScript或VBScript动态生成HTML,根据父ID关系决定节点的嵌套层次。 - 可以使用CSS样式...
动态遍历树形菜单通常用于显示层级结构的数据,例如文件系统、组织结构或者权限管理等。 首先,我们来看前端部分。在jsp页面中,需要引入layui的CSS和JavaScript库,并且注册layui的`layer`和`tree`模块。`layer`...
在SQL Server中,递归...通过CTE,我们可以有效地查询出层级关系中的子节点和父节点,同时利用系统视图和扩展属性来获取表的相关描述和结构信息。在实际工作中,熟练掌握这些技巧将对数据库管理和数据分析大有裨益。
递归查询是一种在数据库中处理层次数据的方法,它通过自身调用来遍历层级结构。在菜单树场景中,每个菜单项可能有子菜单项,形成一种树状结构。通过递归查询,我们可以获取到所有级别的菜单项,包括它们的父级和子级...
当树结构层次较深时,递归查询会导致大量的SQL操作,增加数据库负载,尤其是处理大规模的树结构时,这可能成为性能瓶颈,限制了树结构的扩展性。 接下来,我们看看预排序遍历树方式。MPTT通过在每个节点上增加两个...
1. **递归方式**:这是最常见的方法,通过递归函数遍历整个分类树,每次递归时为当前节点添加父节点的ID或名称,形成路径。这种方法简单直观,但当分类数量巨大时,可能会导致大量的函数调用,影响性能。 2. **栈...
在Oracle数据库环境中,"通过父编码信息给子节点分组"通常涉及到树形结构数据的处理,这在很多业务场景中都很常见,如组织架构、产品分类、地区层级等。这种问题的关键在于如何利用数据库的查询功能来构建并展现这种...
在桌面应用中,如WPF或WinForms,可以利用控件如TreeView,通过数据绑定和递归模板来生成无限级结构。 ```xml <!-- WPF 示例 --> HierarchicalDataTemplate x:Key="MenuItemTemplate"> <Binding Path=...
在VC++编程中,遍历文件夹并自动生成目录树是一项常见的任务,它涉及到文件系统操作和数据结构的使用。这个任务通常用于构建文件管理器、进行文件搜索或实现类似资源管理器的功能。下面我们将详细讲解如何在VC++中...
通过设计这样的字段,我们可以利用数据库的关联查询,而不用编写递归函数来遍历分类结构。 接下来,文章中提供的操作代码展示了如何利用SQL语句来实现无限级分类的查询和展示: 1. 查询无限级分类并展示: ```php...
树型框是一种常见的图形用户界面元素,它可以以层次结构显示数据,非常适合用来表示数据库中的表结构和记录。 在Visual Basic (VB) 或 Visual Studio .NET (C#、VB.NET)等环境中,我们可以利用ADO.NET库来连接和...
它利用数据库的`lft`和`rgt`字段来表示树结构,通过非递归的迭代处理,降低了内存消耗,提升了性能。在CodeIgniter框架下,这种实现方式尤其适合处理大型数据集,为开发人员提供了一种强大的工具来管理具有层次结构...
2. **路径枚举法**:在分类表中增加一个`path`字段,存储分类在整个树中的路径,如“0,1,3”表示第三级分类。查询时,根据路径快速定位到某个分类及其上下文。 在编程实现上,可以使用多种语言,如Java、Python、...
【哈夫曼编码】是一种基于二叉树的数据压缩方法,由数据结构中的“最小带权路径长度”(Minimum Weight Path Length, MWPL)原理构造。在哈夫曼编码中,出现频率较高的字符会得到较短的编码,而出现频率较低的字符则...
在`traverseJson`方法中,我们递归地遍历JSON节点。如果是对象,我们遍历其字段;如果是数组,我们遍历其元素;其他情况则打印出节点的值。 4. **打印输出**:遍历过程中,我们可以根据需要对JSON数据进行处理,...