项目开发过程中遇到一个需要级联操作的需求:关闭或删除一个父级,其对应子集也要随之一起修改,由于之前没遇到过,所以在这个问题上卡了很久,通过度娘发现大多数都是通过一些复杂的sql实现(太菜了sql只会增删改查),时间有限所以决定自己想办法利用java实现需求。
问题分析
首先放出数据库字段:
| id | department_name | is_close | is_delete | parent_id |
|---|---|---|---|---|
| 1 | 外科 | 0 | 0 | 0 |
| 2 | 神经外科 | 0 | 0 | 1 |
| 3 | 神经外科A | 0 | 0 | 2 |
起初,刚接触这个需求的时候把问题想的太简单了,直接sql(以修改1为例):
UPDATE
`department`
SET
is_close=1
WHERE
id=1 AND parent_id=1 AND is_delete=0
但是实际需求是修改1的关闭状态,1的子科室2和2的子科室3都要关闭,这里只给了三层级联,实际可能还有神经外科A1、神经外科A1-1等等,因此这个需求远比我当时想的复杂。
思路构建
需求清楚了以后接下来就是如何用java去实现这个需求:
-
首先,要用java实现肯定要把所有的科室数据拉下来;还有一种思路是先拉下所有父级科室,也就是判断parent_id是否为0,但是这样的话后面可能会频繁访问数据库到库中进行查找,因此这种没有展开去思考
-
取到数据后,开始思考要如何去处理数据列表获取到所有要操作的id,思考的时候突然想到之前数据结构与算法课程中遇到的一个括号匹配算法题,受到这道题的启发想到可以利用队列把所有父级id放进去,然后利用递归对队列中的id一个一个进行匹配,找到之后要操作修改的id放到一个set中,最后再用sql使用in关键字批量修改。
代码实现
思路清晰后开始代码的实现:
- 首先改写修改逻辑代码
// 获取全部科室列表
List<Department> departmentList = departmentDao.findAllDepartments();
// Set存放最终要处理的id列表
Set<Integer> ids = new HashSet<>();
// 队列存放待查找子科室的父级id
Queue<Integer> parentIds = new ConcurrentLinkedDeque<>();
// 将传入的要删除的id放入队列及Set
ids.add(id);
parentIds.add(id);
// 调用递归查找方法,方法逻辑代码看下面
getAllChildId(parentIds, ids, departmentList);
// 对Set中id对应数据执行逻辑删除
departmentDao.deleteDepartment(ids);
- 实现递归查找子科室id方法
/**
* 获取所有子科室id
*
* @param parentIds 待查找子科室的父id队列
* @param ids 子科室id Set
* @param departmentList 科室列表
*/
private void getAllChildId(Queue<Integer> parentIds, Set<Integer> ids, List<Department> departmentList) {
// 父级id队列中没有id,结束递归
if (parentIds.isEmpty()) {
return;
}
// 出队一个,遍历查找子科室,有子科室,子科室id入队准备作为父级id查找子科室
Integer pid = parentIds.poll();
for (Department department : departmentList) {
if (department.getParentId().equals(pid)) {
ids.add(department.getId());
parentIds.add(department.getId());
}
}
// 开始下一个父id
getAllChildId(parentIds, ids, departmentList);
- Dao层sql批量逻辑删除
<update id="delete">
UPDATE `department`
SET is_delete=1
<where>
id IN
<foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
AND is_delete=0
</where>
</update>