要删除树形结构的节点,需考虑两件事:
1.要删除的节点若包含子节点,则需删除该节点及其所有子节点;
2.如果要删除的节点的父节点不包含除该节点外的其它子节点,则该父节点变为叶子;
另外,这两件事需要包含在同一事务中,具有原子性。
下面,先来考虑第一件事:
要遍历树形结构中的一个节点及其子节点,采用递归会比较方便,首先需要一个方法,来将这两件事包含在一起,于是要构建一个方法delClasses
/**
* 根据classes_id 删除班级节点
* @param classes_id
*/
public void delClasses(int classes_id) {
Connection conn = null;
try {
conn = DbUtil.getConnection();
//设置手动提交事务,两个事务需使用同一个Connection
conn.setAutoCommit(false);
//设置班级节点的父节点的leaf
setLeafByDel(conn,classes_id);
//方法复用
delClasses(conn,classes_id);
//两件事完成后,提交事务
conn.commit();
} catch(Exception e) {
//出现异常,则回滚事务
DbUtil.rollBack(conn);
e.printStackTrace();
} finally {
DbUtil.close(conn);
}
}
然后,再考虑删除班级的主方法delClasses(Connection conn,int classes_id),在设计该方法前,需要做些准备,需要一个方法来得到该节点下所有子节点。该方法需要返回Class类型的集合类对象。
/**
* 根据父节点id的到所有的classes对象
* @param pid 当前节点的id
* @return 所有子节点的对象
* @throws SQLException
*/
private List<Class> findClassesByPid(int pid) throws SQLException {
//定义集合类用来装子节点对应的class对象
List<Class> classesList = new ArrayList<Class>();
Connection conn = null;
PreparedStatement psta = null;
ResultSet rs = null;
String sql = "use 成绩管理系统 select * from t_class where pid = ?";
conn = DbUtil.getConnection();
try {
psta = conn.prepareStatement(sql);
psta.setInt(1, pid);
rs = psta.executeQuery();
while(rs.next()) {
Class classes = new Class();
classes.setClassId(rs.getInt("class_id"));
classes.setClassName(rs.getString("class_name"));
classes.setLeaf(rs.getInt("leaf"));
classes.setPid(rs.getInt("pid"));
classesList.add(classes);
}
} finally {
DbUtil.close(rs);
DbUtil.close(psta);
DbUtil.close(conn);
}
//返回子节点对象集合
return classesList;
}
下面,可以写递归删除当前节点及其子节点的方法了。
/**
* 删除班级,递归实现,删除该节点下所有子节点
* @param conn
* @param classes_id 要删除的班级编号
* @throws SQLException
*/
public void delClasses(Connection conn,int classes_id) throws SQLException {
String sql = "use 成绩管理系统 delete from t_class where class_id = ?";
PreparedStatement psta = null;
//根据当前节点的id得到当前节点对应的对象,该方法在上一个例子中已经定义
int leaf = findClassesById(classes_id).getLeaf();
try {
psta = conn.prepareStatement(sql);
psta.setInt(1, classes_id);
psta.executeUpdate();
//当前节点不是叶子节点,则需删除其所属的所有子节点
if(leaf == 0) {
//得到该节点下的所有子节点,存放在classesList中
List<Class> classesList = findClassesByPid(classes_id);
//遍历集合类classesList,递归调用删除方法
for(Iterator<Class> iter = classesList.iterator(); iter.hasNext();) {
Class classes = iter.next();
delClasses(conn,classes.getClassId());
}
}
} finally {
DbUtil.close(psta);
}
}
删除完成之后,需要考虑另外一个问题,即该节点父节点的leaf问题,要解决这个问题,首先需要得到该节点的父节点,再得到该父节点所有的子节点,最后根据该父节点所有子节点的个数来决定设置leaf。
首先,需要一个方法来得到该节点的父节点。该方法的参数为当前节点的pid,即其父节点的id
/**
* 根据当前节点返回其父节点的class对象
* @param classes_id 当前节点id
* @return 当前节点的父节点对象
* @throws SQLException
*/
private Class findParentBySon(int classes_id) throws SQLException {
Class classes = new Class();
classes = findClassesById(classes_id);
String sql = "use 成绩管理系统 select * from t_class where class_id = ?";
PreparedStatement psta = null;
Connection conn = null;
ResultSet rs = null;
try {
conn = DbUtil.getConnection();
psta = conn.prepareStatement(sql);
//将当前节点的pid传入sql语句
psta.setInt(1, classes.getPid());
rs = psta.executeQuery();
while(rs.next()) {
classes.setClassId(rs.getInt("class_id"));
classes.setClassName(rs.getString("class_name"));
classes.setLeaf(rs.getInt("leaf"));
classes.setPid(rs.getInt("pid"));
}
return classes;
} finally {
DbUtil.close(rs);
DbUtil.close(psta);
DbUtil.close(conn);
}
}
然后,再来写修改父节点leaf的方法,该方法应放于删除方法之前执行,即先修改父节点的leaf,再执行删除操作。所以要判断将删除的节点的父节点的子节点的个数是否为1,若为1,则将父节点设置为叶子。若父节点的子节点个数大于1,则不需要设置父节点的leaf。
/**
* 根据删除的节点,设置父节点的leaf,若父节点没有其他子节点,则将父节点设置为叶子;否则不变
* @param conn
* @param classes_id 子节点的id
* @throws SQLException
*/
private void setLeafByDel(Connection conn,int classes_id) throws SQLException {
//用来装父节点所有的子节点
List<Class> classesList = new ArrayList<Class>();
Class classes = new Class();
try {
//得到父节点对象
classes = findParentBySon(classes_id);
//得到父节点所有的子节点,存放在classesList中
classesList = findClassesByPid(classes.getClassId());
//判断classesList 的元素个数,如为1,则需要将该父节点设置为叶子
if(classesList.size() == 1) {
//设置父节点为leaf
modifyLeaf(conn,classes.getClassId(),1);
}
} finally {
null;
}
}
删除节点结束