理解重点:
select的含义是想给children赋值可以调用一个方法,并把这个方法执行结果赋值给children。
column的含义是:其引用的值作为当前select方法执行的参数,让select执行他调用的方法,并且把结果赋值给引用的collection的children
一.数据库
先来看看我们的数据库
注意:
1.我们的数据库是一张三级联动的表,类似于省市县的写法
2.我们的depPath是用部门的id一个一个往后拼接起来的。
3.每一列的depPath的最后一个数字就是我们当前列的部门的id,depPath的倒数第二个数字就是我们当前列部门的父部门的id
二.业务需求
当前的业务需求是对此表进行查询,查询出整个部门树的结构,并在任意一个子节点可以进行增删操作。
三.查询出整个部门树
怎么查询一个树?
1.首先一定是从头到位把这一个树给查询出来,即从最开始的那一列,id=1 ,parentId=-1的那一列开始进行查询。
2.从数据图中我们不难分析出两点:关键点一:子部门的parentId =父部门的id ,
关键点二:depPath的最后一个数字=当前列的id
3.由关键点一,我们就有了头绪,通过 父部门parentId =子部门 id 进行递归查询我们能够查询出来这个树,如果要递归查询查询的话我们包装一个与Department类一模一样的子类DepartmentChildrenVO来进行递归查询是不是十分合适。
4.但是如何去利用mybatis在mapper.xml进行递归查询。
3.1 包装类
@Data
public class DepartmentChildrenVO extends Department{
List <Department> children;
}
3.2 controller,service,mapper简写
controller层
@GetMapping("/")
public List<DepartmentChildrenVO> getAllDepts (){
return departmentService.getAllDepts();
}
service层
List<DepartmentChildrenVO> getAllDepts();
serviceImpl接口实现类
@Override
public List<DepartmentChildrenVO> getAllDepts() {
return departmentMapper.getAllDepts(-1);
}
mapper层
List<DepartmentChildrenVO> getAllDepts(int pid);
3.3 关键—mapper.xml
1.映射的实体类还是DepartMent
2.定义了包装类,作为部门树的子部门,
3.ofType指定的是 映射到list集合属性中pojo的类型
4.在里面引入了一个参数select,指定了引入一个mapper的语句getAllDepts,也就是
我们这里的查询语句,进行递归查询,从头查询到尾部。select的含义是想给children赋值可以调用一个方法,并把这个方法执行结果赋值给children是,column的含义是:其引用的值作为当前children方法执行的参数,让select执行他调用的方法,并且把结果赋值给引用的collection的children。
5.id这个id是当前部门的id,也是我们通过getAllDepts传入parentId查询到的当前部门的id,
把他当做下一个查询的pid丢进来进行查询,实现递归查询
<mapper namespace="com.huang.vhr.framework.web.mapper.DepartmentMapper">
<!--这里正常的把DepartMent映射进来,说到底包装类DepartmentChildrenVO
的数据还是DepartMent里的数据 -->
<resultMap id="BaseDepartMents" type="com.huang.vhr.framework.web.entity.Department">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="enabled" column="enabled"/>
<result property="depPath" column="depPath"/>
<result property="isParent" column="isParent"/>
<result property="parentId" column="parentId"/>
</resultMap>
<!--
定义一个可以迭代的映射,给他取名为DepartMentChildren,类型一定是DepartmentChildrenVO
这个名为DepartMentChildren的映射一定是继承DepartMent的
说到底是用到DepartMent里面的数据进行递归查询
-->
<resultMap id="DepartMentChildren"type="com.huang.vhr.framework.web.entity.DepartmentChildrenVO"
extends="BaseDepartMents">
<!--关键点来了:
1.定义了包装类,作为部门树的子部门,
2.ofType指定的是 映射到list集合属性中pojo的类型
3.在里面引入了一个参数select,指定了引入一个mapper的语句getAllDepts,也就是
我们这里的查询语句,进行递归查询,从头查询到尾部。
4.id这个id是当前部门的id,也是我们通过getAllDepts传入parentId查询到的当前部门的id,
把他当做下一个查询的pid丢进来进行查询,实现递归查询
-->
<collection property="children" ofType="com.huang.vhr.framework.web.entity.Department"
select="com.huang.vhr.framework.web.mapper.DepartmentMapper.getAllDepts"
column="id">
</collection>
</resultMap>
<select id="getAllDepts" resultMap="DepartMentChildren">
select *
from department
WHERE parentId = #{pid}
</select>
</mapper>
四.部门树增加节点
4.1 存储过程实现
编写存储方法的过程,数据库中编写
# 修改数据库中命令的结束符号
DELIMITER $$
USE `vhr`$$
DROP PROCEDURE IF EXISTS `addDep`$$
# 五个参数 in 表示这是一个输入参数,out 表示这是一个输出参数
CREATE DEFINER=`root`@`localhost` PROCEDURE `addDep`(in depName varchar(32),in parentId int,in enabled boolean,out result int,out result2 int)
# 开始存储过程的定义
begin
# 声明一个变量
declare did int;
declare pDepPath varchar(64);
# 执行插入操作
insert into department set name=depName,parentId=parentId,enabled=enabled;
# 查询受影响的行数,并将查询结果赋值给 result 变量
select ROW_COUNT() into result;
# 查询刚刚插入记录的 id 并将查询结果赋值给 did 变量
select LAST_INSERT_ID() into did;
set result2=did;
# 查询父部门的 depPath,并将查询结果赋值给 pDepPath 这个变量
select depPath into pDepPath from department where id=parentId;
# 更新刚刚插入记录的 depPath,concat 是一个字符串拼接函数
update department set depPath=CONCAT(pDepPath,'.',did) where id=did;
# 修改父部门的 isParent 属性
update department set isParent=true where id=parentId;
# 存储过程定义结束
end$$
DELIMITER ;
4.1.1 定义实体类AddDepartmentVO
@Data
public class AddDepartmentVO extends Department{
private Integer result;
}
4.1.2 controller,service,mapper写法
controller层
/**存储过程写法
*
* @param department
* @return
*/
@PostMapping("/add")
public ResponseCode addDepartment(@RequestBody AddDepartmentVO department) {
return departmentService.addDepartment(department);
}
service层
List<DepartmentChildrenVO> getAllDepts();
service的接口实现类
@Override
public ResponseCode addDepartment(AddDepartmentVO department) {
department.setEnabled(true);
department.setIsParent(false);
departmentMapper.addDepartment(department);
if (department.getResult() == 1) {
return ResponseCode.ok("添加成功", department);
}
return ResponseCode.error("添加失败");
}
mapper层
void addDepartment(AddDepartmentVO department);
4.1.3 mappe.xml
<insert id="addDepartment" statementType="CALLABLE">
call addDep(#{name,jdbcType=VARCHAR,mode=IN}, #{parentId,jdbcType=INTEGER,mode=IN},
#{enabled,jdbcType=BOOLEAN,mode=IN}, #{result,jdbcType=INTEGER,mode=OUT},
#{id,jdbcType=INTEGER,mode=OUT})
</insert>
4.2 业务进行实现
思想:
业务写法,从前端拿到父部门的id,就是我们的parentId
1.通过parentId找到副部们的depPath,
2.加上我们的id,id自增长,(同时插入用户名,得到我们的这一条数据,再查询到这个id
3.再更新我们的depPath就是我们的
1.查询要插入用户是否存在,
2.通过parentId查找父部门Dep
3.插入用户,除去dep和id其他四个插入,其他四个中,有 parentId和name我们知道的
4.查询插入的用户,获取返回的id
5.id+父dep = 现在的dep
6.插入dep
业务实现的步骤思想也就是我们存储过程的思想
4.2.1 controller,service,mapper
controller层
@PostMapping("/add1")
public ResponseCode addDepartment1(@RequestBody Department department){
return departmentService.addDepartment1(department);
}
service层
ResponseCode addDepartment1(Department department);
serviceImp
serviceImpl 接口的实现类
@Override
public ResponseCode addDepartment1(Department department) {
department.setEnabled(true);
department.setIsParent(false);
//先查询要插入的用户是否存在
if (departmentMapper.selectDptByPidAndName(department.getParentId(),department.getName())!=null){
return ResponseCode.error("插入失败,用户存在");
}
//查询父部门的depPath
Department fatherDpt = departmentMapper.selectFatherDptByPid(department.getParentId());
if (fatherDpt==null){
return ResponseCode.error("父用户不存在,查询失败");
}
if (departmentMapper.insertDepartment(department)!=1){
return ResponseCode.error("获取id的插入失败");
}
//获取id
int id =departmentMapper.selectDptId(department.getParentId(),department.getName());
String nowDepPath = fatherDpt.getDepPath()+'.'+id ;
if (departmentMapper.updateDptDepPath(nowDepPath,id)==1){
return ResponseCode.ok("成功");
}
return ResponseCode.ok("增加失败");
}
mapper用到的方法
void addDepartment(AddDepartmentVO department);
Department selectDptByPid(@Param("parentId") Integer parentId);
Integer selectDptId(@Param("parentId")Integer parentId,@Param("name")String name);
Department selectDptByPidAndName(@Param("parentId")Integer parentId,@Param("name") String name);
int insertDepartment(Department department);
Department selectFatherDptByPid(Integer parentId);
int updateDptDepPath(@Param("DepPath") String DepPath,@Param("id") int id);
mapper.xml用到的方法
<select id="selectDptByPid" resultType="com.huang.vhr.framework.web.entity.Department">
select *
from department where parentId=#{parentId};
</select>
<insert id="insertDepartment">
insert into department ( name, parentid, enabled, isparent) values (#{name,jdbcType=VARCHAR},#{parentId},#{enabled,jdbcType=BOOLEAN},#{isParent})
</insert>
<select id="selectFatherDptByPid" resultType="com.huang.vhr.framework.web.entity.Department">
select * from department where id =#{parentId}
</select>
<update id="updateDptDepPath">
update department set DepPath = #{DepPath} where id =#{id};
</update>
<select id="selectDptByPidAndName" resultMap="BaseDepartMents">
select * from department where parentId =#{parentId} and name =#{name};
</select>
<select id="selectDptId" resultType="java.lang.Integer">
select id from department where parentId =#{parentId} and name=#{name}
</select>
五.部门树删除节点
删除的思想:
1.传入id,查询有没有子部门
2.有子部门无法删除
3.无子部门可以删除
4.再去查询当前部门的父部门还有无子部门
5.无,父部门的isParent改成1
6.有,直接结束
5.1 存储过程实现
DELIMITER $$
USE `vhr`$$
DROP PROCEDURE IF EXISTS `deleteDep`$$
#实际要编写的步骤
# in did int 输入参数为int类型,你要删除的部门的id , out result int输出参数为int类型,你要删除的部门的结果
CREATE DEFINER=`root`@`localhost` PROCEDURE `deleteDep`(in did int,out result int)
begin
# ecount保存部门下的员工数量
declare ecount int;
# 要删除部门的父部门的id
declare pid int;
# 要删除的部门下的子部门的数量
declare pcount int;
#
declare a int;
# 满足条件id=did 且 isParent=false 的部门的数量赋值给 a,即如果你的传入id为did的时候你的要删除部门isParent为false(其下无子部门),
#这时候的查询结果如果不是1,即上面两个条件不满足,(-2,表示当前部门不存在,或者说)
#或者统计到的a!=1(查询结果赋值给了a),即当前部门不满足上面两个条件,就自动的判定为false就说明当前部门无法删除
select count(*) into a from department where id=did and isParent=false;
if a=0 then set result=-2;
else
#查询要删除的部门下的员工数量,有员工则不能够删除
select count(*) into ecount from employee where departmentId=did;
#-1表示部门下有员工,删除失败
if ecount>0 then set result=-1;
else
#查询要删除部门的父部门id,并将结果赋值给pid
select parentId into pid from department where id=did;
#执行删除操作
delete from department where id=did and isParent=false;
#查询手影响的行数
select row_count() into result;
#查询被删除部门的父部门下的子部门的数量
select count(*) into pcount from department where parentId=pid;
#将父部门的IsParent设置为 false
if pcount=0 then update department set isParent=false where id=pid;
end if;
end if;
end if;
end$$
DELIMITER ;
5.2 controller,service,mapper
controller 层
@DeleteMapping("/{id}")
public ResponseCode deleteDepartmentById(@PathVariable Integer id) {
return departmentService.deleteDepartmentById(id);
}
service层
ResponseCode deleteDepartmentById(Integer id);
serviceImpl接口的实现类
@Override
public ResponseCode deleteDepartmentById(Integer id) {
AddDepartmentVO addDepartmentVO = new AddDepartmentVO();
addDepartmentVO.setId(id);
departmentMapper.deleteDepartmentById(addDepartmentVO);
if (addDepartmentVO.getResult() == -2) {
return ResponseCode.error("该部门下有子部门或者该部门不存在,删除失败");
} else if (addDepartmentVO.getResult() == -1) {
return ResponseCode.error("该部门下有员工,删除失败");
} else if (addDepartmentVO.getResult() == 1) {
return ResponseCode.ok("删除成功");
}
return ResponseCode.error("删除失败");
}
mapper层
void deleteDepartmentById(AddDepartmentVO addDepartmentVO);
5.3 mapper.xml
<select statementType="CALLABLE" id="deleteDepartmentById">
<!--依次填充入我们存储过程中的语句-->
call deleteDep(#{id,jdbcType=INTEGER,mode=IN},# {result,jdbcType=INTEGER,mode=OUT})
</select>
5.4 业务实现
具体业务步骤:
1.通过传入的id查询当前部门的isParent,为0则继续下面,为1则说明其下还有子部门结束业务
2.通过传入的id找到parentId
3.通过parentId找到parnetId的总数
4.parentId总数>1 ,可以删除,且不用改父部门的isParent,直接删除
5.parentId总数=1 ,可以删除,要把父部门的isParent改成false
6.通过parentId找到父部门的id
7.再通过id去修改父部门的IsParent
黄黄这里就不写了,跟上面没啥差别的