基于数据库的无限级树形结构后台(非递归遍历节点,通过增加维护节点PATH字段)...


/**
*
* @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);
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值