无限分类是我们项目中经常使用的功能, 这里基于ssm(Spring SpringMVC MyBatis)整合一个完善的后台分享给大家使用
效果如下:(添加,删除,修改,移动类别到任意目录下等功能)

一. 创建表
CREATE TABLE `sorts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `sortName` varchar(50) NOT NULL COMMENT '类别名称', `parentId` int(11) NOT NULL DEFAULT '0' COMMENT '父级id', `sortPath` varchar(150) NOT NULL COMMENT '父级到当前类别的所有路径且多一个逗号(模糊搜索有用)', `levels` int(11) NOT NULL DEFAULT '1' COMMENT '层级, 默认1, 表示第一级', `orders` int(11) NOT NULL DEFAULT '1' COMMENT '排序', PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=66 DEFAULT CHARSET=utf8;

二. pojo 类
package com.pojo;
public class Sorts {
private int id;
private String sortName;
private int parentId;
private String sortPath;
private int levels;
private int orders;
private String space; //列表查询时 前面的 间隔
public int getId() {
省略setter and getter
}三. Dao类
package com.dao;
import com.pojo.Sorts;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.HashMap;
@Repository("sortsDao")
@Mapper
public interface SortsDao {
public int addSorts(Sorts sorts); //返回添加信息的主键
public Sorts selectBySortNameAndParendId(Sorts sorts);
//查询 当前父类下 是否已经有相同的类别
public Sorts selectSortsById(Integer id);
public int updateSorts(Sorts sorts);
public int updateSortsBySortNameOrdersId(Sorts sorts);
public ArrayList<Sorts> selectByParentId(Integer id);
//得到当前类别id下的所有子类
public Sorts selectSortsByParentIdSortNameId(Sorts sorts);
//查询非当前id下,同一个父类下是否有相同的类别
public int updateAllChildSorts(HashMap<String,Object> hm);
//更新所有的修改类别的子类
public int updateAllChildSortsLevels(HashMap<String,Object> hm);
//删除当类类别及所有的子类
public int deleteAllSortsById(Integer id);
}四. mapper文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhuangzi.ssm0925.dao.SortsDao">
<!-- 添加一个类别 -->
<insert id="addSorts" parameterType="sorts" useGeneratedKeys="true" keyProperty="id">
insert into sorts (sortName,parentId,sortPath,levels,orders) values
(#{sortName},#{parentId},#{sortPath},#{levels},#{orders})
</insert>
<!-- 查询当前父类下是否存在相同的类别名称-->
<select id="selectBySortNameAndParendId" parameterType="sorts" resultType="sorts" >
select * from sorts where parentId = #{parentId} and sortName = #{sortName}
</select>
<!-- 查询当前父类下不是原来的ID下是否存在相同的类别名称-->
<select id="selectSortsByParentIdSortNameId" parameterType="sorts" resultType="sorts" >
select * from sorts where parentId = #{parentId} and sortName = #{sortName} and id <> #{id}
</select>
<!-- 根据ID查询类别-->
<select id="selectSortsById" parameterType="Integer" resultType="sorts" >
select * from sorts where id = #{id}
</select>
<!-- 得到当前类别下面所有的子类-->
<select id="selectByParentId" parameterType="Integer" resultType="sorts">
select * from sorts where parentId = #{id} order by orders asc
</select>
<!-- 修改类别 -->
<update id="updateSorts" parameterType="sorts">
update sorts set sortName = #{sortName},
parentId = #{parentId},
sortPath = #{sortPath},
levels = #{levels},
orders = #{orders}
where id = #{id}
</update>
<!-- 父id不变, 则parentid, levels都不需要修改-->
<update id="updateSortsBySortNameOrdersId" parameterType="sorts">
update sorts set sortName = #{sortName},
orders = #{orders}
where id = #{id}
</update>
<update id="updateAllChildSorts" parameterType="Map">
update sorts set sortPath = replace(sortPath,#{oldSortPath},#{newSortPath}) where
sortPath like concat('%',#{oldSortPath},'%') and id <> #{id}
</update>
<!-- //所有的 逗号 的个数 减一 即levels-->
<update id="updateAllChildSortsLevels" parameterType="Map">
update sorts set levels = length(sortPath)- length(replace(sortPath,',','')) - 1 where
sortPath like concat('%',#{newSortPath},'%') and id <> #{id}
</update>
<!-- 删除当类类别及所有的子类-->
<delete id="deleteAllSortsById" parameterType="Integer">
delete from sorts where sortPath like concat ('%',',',#{id},',','%')
</delete>
</mapper>五. service接口
package com.service;
import com.pojo.Sorts;
import java.util.ArrayList;
public interface SortsService {
public String addSorts(Sorts sorts);
public ArrayList<Sorts> getChildren(int parentId, String space, ArrayList<Sorts> arr);
public StringBuffer selectTrees(int pid,String selectName,int currentId);
public Sorts selectSortsById(Integer id);
public String updateSorts(Sorts sorts);
public String deleteAllSortsById(Integer id);
}六. service实现类
package com.zhuangzi.ssm0925.service;
import com.zhuangzi.ssm0925.dao.SortsDao;
import com.zhuangzi.ssm0925.entity.Sorts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
@Service("sortsService")
public class SortsServiceImpl implements SortsService{
public static int initLevel = 0;
@Autowired
private SortsDao sortsDao;
public static ArrayList<Sorts> all; //存储所有的查询子类数据
@Override
public String addSorts(Sorts sorts) {
//判断类名不能为空
if(sorts.getSortName().equals("")) {
return "<script>alert('类别名称不能为空');history.back();</script>";
}
//判断同一级下不能重复的名称
if(sortsDao.selectBySortNameAndParendId(sorts) != null){
return "<script>alert('父类下已存在相同类别!!!');history.back();</script>";
}
//设置level, sortpath 信息 begin
sorts.setLevels(1);
sorts.setSortPath("0,");
if(sorts.getParentId() != 0){
//最顶级时使用默认设置值, 否则根据父级来决定
Sorts getSorts = sortsDao.selectSortsById(sorts.getParentId());
sorts.setLevels(getSorts.getLevels() + 1);
sorts.setSortPath(getSorts.getSortPath());
//暂时为父级sortPath, 当添加后, 再更新,将当前的id链接上
}
//将信息添加到数据库sorts
sortsDao.addSorts(sorts); //sorts中会更新为新增加的id主键
//更新sortPath 到数据库
sorts.setSortPath(sorts.getSortPath() + sorts.getId() + ",");
sortsDao.updateSorts(sorts);//
return "<script>alert('添加成功');location.href='/admin/sorts/list';</script>";
}
//parentId: 父类 ID, 查询当前类别下的所有子类
//space : // 类别前面的空隔
@Override
public ArrayList<Sorts> getChildren(int parentId, String space, ArrayList<Sorts> arr){
if(arr == null){
//第一次查询
System.out.println(parentId);
arr = sortsDao.selectByParentId(parentId);
initLevel = arr.get(0).getLevels();
all = new ArrayList<>(); //第一次时, 初始化, 避免刷新调用重新添加信息
}
if(arr != null){
for(Sorts sorts : arr){
//根据级别不一样, 在前面加上相应的分隔符
int levels = sorts.getLevels();
if(levels == initLevel){
sorts.setSpace("");
}
else if(levels == initLevel + 1){
space = " |---->";
sorts.setSpace(space);
}
else{
sorts.setSpace(space);
}
all.add(sorts); //将所有的信息都加到all里, 并增加一个space字段
parentId = sorts.getId();
ArrayList<Sorts> arr_child = sortsDao.selectByParentId(parentId);
if(arr_child != null){
getChildren(parentId," |" + space,arr_child);
}
}
}
return all;
}
//得到无限分类的select下拉列表
//pid : 父id
//selectName select的名字
//currenId : 被选择的id, 没有的话,传递0
@Override
public StringBuffer selectTrees(int pid,int currentId){
StringBuffer sBuffer = new StringBuffer();
ArrayList<Sorts> results = getChildren(pid,"",null);
if(results != null){
for(Sorts sorts : results){
int id = sorts.getId();
if( id == currentId){
sBuffer.append("<option value='"+id+"' style='background:#E20A0A; color:#fff;' selected>"+
sorts.getSpace() + sorts.getSortName() + "</option> \n");
}
else{
sBuffer.append("<option value='"+id+"'>"+ sorts.getSpace() + sorts.getSortName() + "</option> \n");
}
}
}
return sBuffer;
}
@Override
public Sorts selectSortsById(Integer id) {
return sortsDao.selectSortsById(id);
}
@Override
public String updateSorts(Sorts sorts) {
//判断类名不能为空
if(sorts.getSortName().equals("")) {
return "<script>alert('类别名称不能为空');history.back();</script>";
}
//根据id得到原来的信息
Sorts oldSorts = sortsDao.selectSortsById(sorts.getId());
int oldPid = oldSorts.getParentId(); //原父id
String oldSortPath = oldSorts.getSortPath();
//判断类别名称是否重复
Sorts exitSorts = sortsDao.selectSortsByParentIdSortNameId(sorts);
if(exitSorts != null){
return "<script>alert('类别已经存在,请更换类别名称');history.back();</script>";
}
if(oldPid == sorts.getParentId()){
//父id不变, 则parentid, level都不需要修改
sortsDao.updateSortsBySortNameOrdersId(sorts);
}
else if(sorts.getParentId() == 0){
//移动到一级类别
sorts.setSortPath("0," + sorts.getId() + ",");
sorts.setLevels(1);
//更新当前类别信息
sortsDao.updateSorts(sorts);
//更新所有的修改类别的子类sortPath
HashMap<String,Object> hm = new HashMap<>();
hm.put("oldSortPath",oldSortPath);
hm.put("newSortPath",sorts.getSortPath());
hm.put("id",sorts.getId());
sortsDao.updateAllChildSorts(hm);
//更新所有子类的层级
sortsDao.updateAllChildSortsLevels(hm);
}
else{
//判断是否移到到了子类另, 这样是不允许的, 中间会断层
//得到当前父类的path
Sorts parentSorts = sortsDao.selectSortsById(sorts.getParentId());
String parentSortPath = parentSorts.getSortPath();
if(parentSortPath.indexOf(oldSortPath) >= 0){
return "<script>alert('类别不能选择为原父类的子类');history.back();</script>";
}
else{
//父类更新到上一级或者其它的类别下, 那么该类别下所有的子类都需要更新父级sortpath
//新的sortpath
String newSortPath = parentSortPath + sorts.getId() + ",";
int newLevel = parentSorts.getLevels() + 1;
sorts.setSortPath(newSortPath);
sorts.setLevels(newLevel);
sortsDao.updateSorts(sorts);
//更新所有的修改类别的子类
//更新所有的修改类别的子类sortPath
HashMap<String,Object> hm = new HashMap<>();
hm.put("oldSortPath",oldSortPath);
hm.put("newSortPath",sorts.getSortPath());
hm.put("id",sorts.getId());
sortsDao.updateAllChildSorts(hm);
//更新所有子类的层级
sortsDao.updateAllChildSortsLevels(hm);
}
}
return "<script>alert('修改成功');location.href='/admin/sorts/list';</script>";
}
//删除所有的子类
@Override
public String deleteAllSortsById(Integer id) {
sortsDao.deleteAllSortsById(id);
return "<script>alert('删除成功');location.href='/admin/sorts/list';</script>";
}
}七. 类别控制器
package controller;
import pojo.Sorts;
import service.SortsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@Controller
public class SortsController {
@Autowired
@Qualifier("sortsService")
private SortsService sortsService;
@GetMapping(value = "/admin/sorts/add")
public String add(@RequestParam(defaultValue = "0") Integer parentId,Model model){
model.addAttribute("selectTrees",sortsService.selectTrees(0,"parentId",parentId));
return "admin/sorts/add";
}
@PostMapping(value = "/admin/sorts/addSave")
public void addSave(Sorts sorts, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println(sortsService.addSorts(sorts));
}
@GetMapping(value = "/admin/sorts/list")
public String list(Model model){
ArrayList<Sorts> all = sortsService.getChildren(0,"",null);
model.addAttribute("all",all);
return "admin/sorts/list";
}
@GetMapping(value = "/admin/sorts/update")
public String update(@RequestParam(defaultValue = "0") Integer id,Model model){
Sorts sorts = sortsService.selectSortsById(id);
model.addAttribute("sorts",sorts);
model.addAttribute("selectTrees",sortsService.selectTrees(0,"parentId",sorts.getParentId()));
return "admin/sorts/update";
}
@PostMapping(value = "/admin/sorts/updateSave")
public void updateSave(Sorts sorts, HttpServletResponse response) throws IOException{
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println(sortsService.updateSorts(sorts));
}
@GetMapping(value = "/admin/sorts/del")
public void del(@RequestParam(defaultValue = "0") Integer id, HttpServletResponse response) throws IOException{
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println(sortsService.deleteAllSortsById(id));
}
}八. 控制器调用类
package com.controller;
import com.pojo.Student;
import com.service.SortsService;
import com.service.StudentService;
import com.service.StudentServiceImpl;
import javafx.scene.chart.ValueAxis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.ArrayList;
@Controller
public class StudentController {
@Autowired
@Qualifier("sortsService")
private SortsService sortsService;
@Autowired
@Qualifier("studentService")
private StudentService studentService;
@GetMapping(value = "/admin/studentAdd")
public String studentAdd(Model model){
model.addAttribute("blood", sortsService.selectTrees(61,"blood",62));
return "admin/student/add";
}
@PostMapping(value = "/admin/studentAddSave")
@ResponseBody
public int studentAddSave(Student student) throws IOException {
return studentService.addStudent(student);
}
//显示学生信息
@GetMapping(value = "/admin/studentList")
public String studentList(Student student, Model model){
ArrayList list = studentService.studentList(student);
model.addAttribute("list",list);
return "admin/student/list";
}
}
九. 其它类调用下拉信息
@GetMapping(value = "/admin/studentAdd")
public String studentAdd(Model model){
model.addAttribute("blood", sortsService.selectTrees(61,62));
return "admin/student/add";
}JSP页面:
显示调用效果:

请把相应的dao, service, controller, mapper及视图html文件放到自己的相应
基于Thymeleaf
