一种以集合的思想实现的一种三级数据联动的方式(后端实现)


前言

这是一种读取excel文件数据以集合的事项来实现数据三级联动的一种实例,使用到的技术稳定,算法简单,但是可靠。期间穿插了一种以parentId来实现三级数据联动的方法,在这里也做了说明。做了这些,前端需要什么样的数据,就返回什么数据,分分钟的事情


提示:以下是本篇文章正文内容,下面案例可供参考

一、级联的示例数据

示例数据我们选取职业数据,比如:金融-投融资-投资顾问,这种三级的数据结构,当然,最简单的三级数据示例大家也可以选择省市区之类的,更直观。这里仅作示例说明,不做其他只用,各路大神定有更好的办法。
引入的依赖包:

import lombok.extern.slf4j.Slf4j;
  import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  import org.apache.poi.ss.usermodel.Row;
  import org.apache.poi.ss.usermodel.Sheet;
  import org.apache.poi.ss.usermodel.Workbook;
  import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.test.context.SpringBootTest;
  import org.springframework.test.context.junit4.SpringRunner;
  import javax.annotation.Resource;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import com.alibaba.fastjson.JSON;
  import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

大概就是这些依赖包,其他的也没啥了,根据环境的不同,大家调整

二、步骤

1.读取excel文件

excel文件里面有表,行,列数据,要分别读取。

frontDo方法先读取文件:

private Workbook frontDo(String excelPath) throws IOException, InvalidFormatException {
        File excel = new File(excelPath);//excelPath是文件所在的路径
        Workbook wb = null;
        if (excel.isFile() && excel.exists()) {
            String[] split = excel.getName().split("\\.");  //.是特殊字符,需要转义!!!!!

            //根据文件后缀(xls/xlsx)进行判断,但是自己的office一定是要激活的,打开的时候不能自动弹出窗口
            if ("xls".equals(split[1])) {
                FileInputStream fis = new FileInputStream(excel);   //文件流对象
                wb = new HSSFWorkbook(fis);
            } else if ("xlsx".equals(split[1])) {
                wb = new XSSFWorkbook(excel);
            } else {
                System.out.println("文件类型错误!");
            }
        }
        return wb;
    }

2.读取文件内数据表

readSheet方法代码如下:

private List<Sheet> readSheet(String url) throws IOException, InvalidFormatException {
        Workbook wb = frontDo(url);
        int sheetSize = wb.getNumberOfSheets();
        List<Sheet> sheets = new ArrayList<Sheet>();
        for (int i = 0; i < sheetSize; i++) {
            Sheet sheet = wb.getSheetAt(i);
            sheets.add(sheet);
        }
        return sheets;
    }

3.读取数据表内的行并把行数据序列化到一个关于行的entity:RowEntity

RowEntity的代码如下:

package data.dictionary.plateform.core.occupation;

    import lombok.Data;

    @Data
    public class RowEntity {
        private String first; //每行的第一列数据
        private String second;//每行的第二列数据
        private String third;//每行的第三列数据
    } 

buildEntity读取每行方法:

private List<RowEntity> buildEntity(String path) throws IOException, InvalidFormatException {
        //"C:\\Users\\yongc\\Desktop\\data\\19.xlsx";
        List<RowEntity> rowEpntitys = new ArrayList<>();
        List<Sheet> sheets = readSheet(path);
        int firstRowIndex = sheets.get(0).getFirstRowNum();
        int lastRowIndex = sheets.get(0).getLastRowNum();
        System.out.println(firstRowIndex);
        System.out.println(lastRowIndex);
        System.out.println();
        for (int rIndex = firstRowIndex; rIndex <= lastRowIndex; rIndex++) {   //遍历行
            Row row = sheets.get(0).getRow(rIndex);
            RowEntity rowEntity = new RowEntity();
            rowEntity.setFirst(row.getCell(0).toString());
            rowEntity.setSecond(row.getCell(1).toString());
            rowEntity.setThird(row.getCell(2).toString());
            rowEpntitys.add(rowEntity);
        }
        return rowEpntitys;
    }

到此,excel表的所有数据都序列化到一个list中了。

4.一级数据序列化:DataEntity

DataEntity类:
这个类也是建表的结构,数据库中这些数据是要入库的。

package data.dictionary.plateform.core.occupation;
   import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
   import com.baomidou.mybatisplus.annotation.TableName;
   import lombok.Data;
   @Data
   @TableName(value = "t_data_occupation",resultMap = "BaseResultMap")
   public class DataEntity {
      @TableId(value = "id",type = IdType.AUTO)
      private int id;
      private String dataName;
      private int dataLeft;
      private int dataRight;
      private int dataDepth;
      private int parentId;
    }

一级数据序列化:

private List<DataEntity>  buildFirst(List<RowEntity> rowEpntitys) {
        List<DataEntity> dataEntities = new ArrayList<>();
        //赋值
        for (int i = 0; i < rowEpntitys.size(); i++) {
            DataEntity dataEntity = new DataEntity();
            dataEntity.setDataName(rowEpntitys.get(i).getFirst());
            dataEntity.setDataDepth(1);
            //dataEntity.setId(s);
            dataEntities.add(dataEntity);
        }
        //去重
        HashSet b = new HashSet(dataEntities);
        dataEntities.clear();
        dataEntities.addAll(b);
        return dataEntities;
    }

5.一级数据保存

这一步的作用也是生成默认的id,保存上一步稳定生成的一级数据结果。

private  List<DataEntity> firstDataPersistence(List<DataEntity> dataEntities){
        occupationDataDAO.insertForEach(dataEntities);
        QueryWrapper<DataEntity> queryWrapper=new QueryWrapper<>();
        queryWrapper.lambda();
        List<DataEntity> dataEntityList=occupationDataDAO.selectList(queryWrapper);
        return  dataEntityList;
    }

6.二级数据序列化并持久保存

这一步的作用也是生成默认的id,保存上一步稳定生成的一级数据结果。

private List<DataEntity> secondDataPersistence(List<RowEntity> rowEpntitys){

       //取出上一步持久到数据库的一级数据,因为只有一级数据在库里,这个时候可以是全查,不过最好加上depth=1的关键字
        QueryWrapper<DataEntity> queryWrapper=new QueryWrapper<>();
        queryWrapper.lambda();
        List<DataEntity> dataEntityList=occupationDataDAO.selectList(queryWrapper);
        //赋值
        for (int i = 0; i < dataEntityList.size(); i++) {
            List<DataEntity> dataEntityListSecond=new ArrayList<>();
            for (int j = 0; j < rowEpntitys.size(); j++) {
                if (dataEntityList.get(i).getDataName().equals(rowEpntitys.get(j).getFirst())) {
                    DataEntity dataEntity=new DataEntity();
                    dataEntity.setDataDepth(2);
                    dataEntity.setDataName(rowEpntitys.get(j).getSecond());
                    dataEntity.setParentId(dataEntityList.get(i).getId());
                    dataEntityListSecond.add(dataEntity);
                }
            }
            //去重
            HashSet c = new HashSet(dataEntityListSecond);
            dataEntityListSecond.clear();
            dataEntityListSecond.addAll(c);
            //入库
            occupationDataDAO.insertForEach(dataEntityListSecond);
        }
        return null;
    }

7.三级数据序列化并持久保存

这一步的作用也是生成默认的id,保存上一步稳定生成的一级数据结果。

private List<DataEntity> thirdDataPersistence(List<RowEntity> rowEpntitys){
       //查出保存到数据库的所有一二级数据结果,并包含一二级数据的关系
        List<DataEntityVO> dataEntityVOS=readService.selectDataEntityVO(null);

       //赋值
        Integer num=0;//计数,随时和对数据条数是否正确
        for (int i = 0; i <dataEntityVOS.size() ; i++) {
            List<DataEntity> dataEntities=new ArrayList<>();
            for (int j = 0; j < rowEpntitys.size(); j++) {
                if (dataEntityVOS.get(i).getParentName().equals(rowEpntitys.get(j).getFirst())&&dataEntityVOS.get(i).getDataName().equals(rowEpntitys.get(j).getSecond())) {
                    DataEntity dataEntity=new DataEntity();
                    dataEntity.setParentId(dataEntityVOS.get(i).getId());
                    dataEntity.setDataName(rowEpntitys.get(j).getThird());
                    dataEntity.setDataDepth(3);
                    dataEntities.add(dataEntity);
                }
            }
            //去重
            HashSet c = new HashSet(dataEntities);
            dataEntities.clear();
            dataEntities.addAll(c);
            num=num+dataEntities.size();
            //入库
            occupationDataDAO.insertForEach(dataEntities);
        }
        System.out.println(num);//打印出数据条数
        return null;
    }

到此已经完成了所有的数据入库,这个时候已经配置了二级三级数据的父级id,这个时候已经可以完成三级联动的操作了
关于集合的思想到此还未有体现,下面开始配置数据的左右边界,这样就可以按照集合的思想来处理三级联动选择数据了。
这里调用了一个接口 readService.selectDataEntityVO(null)
接口:List selectDataEntityVO(DataEntity dataEntity);
接口实现:

@Override
    public List<DataEntityVO> selectDataEntityVO(DataEntity dataEntity) {
        QueryWrapper<DataEntity> queryWrapper=new QueryWrapper<>();
        queryWrapper.lambda().eq(DataEntity::getDataDepth,2);
        List<DataEntity> dataEntityList=occupationDataDAO.selectList(queryWrapper);
        List<DataEntityVO> dataEntityVOS=new ArrayList<>();
        for (int i = 0; i < dataEntityList.size(); i++) {
            DataEntityVO dataEntityVO=new DataEntityVO();
            BeanUtils.copyProperties(dataEntityList.get(i),dataEntityVO);
            QueryWrapper<DataEntity> queryWrapper1=new QueryWrapper<>();
            queryWrapper1.lambda().eq(DataEntity::getId,dataEntityVO.getParentId());
            DataEntity dataEntitiy=occupationDataDAO.selectOne(queryWrapper1);
            dataEntityVO.setParentName(dataEntitiy.getDataName());
            dataEntityVOS.add(dataEntityVO);
        }
        return dataEntityVOS;
    }

8.输出数据的树状结构(全部数据)

这个单独写一个接口来操作:返回的数组里,对象是:DataEntityVO
DataEntityVO类:

package data.dictionary.plateform.core.vo;

  import lombok.Data;
  import java.util.List;

  @Data
   public class DataEntityVO { 
     private int id;
     private String dataName;
     private int dataLeft;
     private int dataRight;
     private int dataDepth;
     private int parentId;
     private String parentName;
     private List<DataEntityVO> dataEntityVOList;
  }
  

接口:List<DataEntityVO> selectDataEntityVOTrees();
方法比较笨,但是逻辑便于理解。
接口实现:

@Override
    public List<DataEntityVO> selectDataEntityVOTrees() {
        List<DataEntityVO> dataEntityVOsF=new ArrayList<>();
        QueryWrapper<DataEntity> queryWrapper1=new QueryWrapper<>();
        queryWrapper1.lambda().eq(DataEntity::getDataDepth,1);
        List<DataEntity> dataEntitiesF=occupationDataDAO.selectList(queryWrapper1);
        for (int i = 0; i < dataEntitiesF.size(); i++) {
            DataEntityVO dataEntityVOF=new DataEntityVO();
            dataEntityVOF.setId(dataEntitiesF.get(i).getId());
            dataEntityVOF.setDataName(dataEntitiesF.get(i).getDataName());
            dataEntityVOF.setDataDepth(1);
            QueryWrapper<DataEntity> queryWrapper2=new QueryWrapper<>();
            queryWrapper2.lambda().eq(DataEntity::getParentId,dataEntitiesF.get(i).getId());
            List<DataEntityVO> dataEntityVOsS=new ArrayList<>();
            List<DataEntity> dataEntitiesS=occupationDataDAO.selectList(queryWrapper2);
            for (int j = 0; j < dataEntitiesS.size(); j++) {
                DataEntityVO dataEntityVOS=new DataEntityVO();
                dataEntityVOS.setId(dataEntitiesS.get(j).getId());
                dataEntityVOS.setDataDepth(2);
                dataEntityVOS.setDataName(dataEntitiesS.get(j).getDataName());
                dataEntityVOS.setParentId(dataEntitiesS.get(j).getParentId());
                QueryWrapper<DataEntity> queryWrapper3=new QueryWrapper<>();
                queryWrapper3.lambda().eq(DataEntity::getParentId,dataEntitiesS.get(j).getId());
                List<DataEntityVO> dataEntityVOsT=new ArrayList<>();
                List<DataEntity> dataEntitiesT=occupationDataDAO.selectList(queryWrapper3);
                for (int k = 0; k < dataEntitiesT.size(); k++) {
                    DataEntityVO dataEntityVOT=new DataEntityVO();
                    dataEntityVOT.setId(dataEntitiesT.get(k).getId());
                    dataEntityVOT.setParentId(dataEntitiesT.get(k).getParentId());
                    dataEntityVOT.setDataName(dataEntitiesT.get(k).getDataName());
                    dataEntityVOT.setDataDepth(3);
                    dataEntityVOsT.add(dataEntityVOT);
                }
                dataEntityVOS.setDataEntityVOList(dataEntityVOsT);
                dataEntityVOsS.add(dataEntityVOS);
            }
            dataEntityVOF.setDataEntityVOList(dataEntityVOsS);
            dataEntityVOsF.add(dataEntityVOF);
        }
        return dataEntityVOsF;
    }

9.配置集合边界

这里配置的集合边界并没有留出余量,如果数据有新增和修改就要重新执行全部的程序,不过无所谓了,这种数据也就是执行一次,资源啊,内存啊啥的,可以忽略。

private List<DataEntity> buildLeftRight(List<DataEntityVO> dataEntityVOS){
        List<DataEntity> dataEntitieAll=new ArrayList<>();
        int numLR=0;
        for (int i = 0; i < dataEntityVOS.size(); i++) {
            //一级
            DataEntity dataEntityF=new DataEntity();
            dataEntityVOS.get(i).setDataLeft(numLR);
            numLR=numLR+1;
            for (int j = 0; j <dataEntityVOS.get(i).getDataEntityVOList().size() ; j++) {
                //二级
                DataEntity dataEntityS=new DataEntity();
                dataEntityVOS.get(i).getDataEntityVOList().get(j).setDataLeft(numLR);
                numLR=numLR+1;
                for (int k = 0; k < dataEntityVOS.get(i).getDataEntityVOList().get(j).getDataEntityVOList().size(); k++) {
                    //三级
                    DataEntity dataEntityT=new DataEntity();
                    dataEntityVOS.get(i).getDataEntityVOList().get(j).getDataEntityVOList().get(k).setDataLeft(numLR);
                    numLR=numLR+1;
                    dataEntityVOS.get(i).getDataEntityVOList().get(j).getDataEntityVOList().get(k).setDataRight(numLR);
                    numLR=numLR+1;
                    BeanUtils.copyProperties(dataEntityVOS.get(i).getDataEntityVOList().get(j).getDataEntityVOList().get(k),dataEntityT);
                    dataEntitieAll.add(dataEntityT);
                }
                dataEntityVOS.get(i).getDataEntityVOList().get(j).setDataRight(numLR);
                numLR=numLR+1;
                BeanUtils.copyProperties(dataEntityVOS.get(i).getDataEntityVOList().get(j),dataEntityS);
                dataEntitieAll.add(dataEntityS);
            }
            dataEntityVOS.get(i).setDataRight(numLR);
            BeanUtils.copyProperties(dataEntityVOS.get(i),dataEntityF);
            dataEntitieAll.add(dataEntityF);
            numLR=numLR+1;
        }
        return dataEntitieAll;
    }

10.按照上一步配置好的数据入库

private void updateLereDataPersistence(List<DataEntity> dataEntitieAll){
        //循环更新,有必要的大神可以做数据库循环,也可一次插入,在这里偷懒了,本类就是一次操作终身受用
		//不要计较太多,先解决有无的问题
        for (int i = 0; i <dataEntitieAll.size() ; i++) {
            occupationDataDAO.updateById(dataEntitieAll.get(i));
        }
    }

11.从数据excel表到数据库完整数据的调用过程,以上方法写完之后,调用这个方法就完成了以上方法的调用。

protected void allDataPersistence(String path) throws IOException, InvalidFormatException {
        List<RowEntity> rowEpntitys=this.buildEntity(path);//1
        List<DataEntity> dataEntities= this.buildFirst(rowEpntitys);//2
        this.firstDataPersistence(dataEntities);//3
        this.secondDataPersistence(rowEpntitys);//4
        this.thirdDataPersistence(rowEpntitys);//5
        List<DataEntityVO> dataEntityVOS=readService.selectDataEntityVOTrees();//6
        this.updateLereDataPersistence(this.buildLeftRight(dataEntityVOS));//7
    }	

12.excel内的数据

在这里插入图片描述

13.其他代码

mybits的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="data.dictionary.plateform.server.dao.OccupationDataDAO">

    <resultMap id="BaseResultMap" type="data.dictionary.plateform.core.occupation.DataEntity">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="data_name" jdbcType="VARCHAR" property="dataName"/>
        <result column="data_left" jdbcType="INTEGER" property="dataLeft"/>
        <result column="data_right" jdbcType="INTEGER" property="dataRight"/>
        <result column="data_depth" jdbcType="INTEGER" property="dataDepth"/>
        <result column="parent_id" jdbcType="INTEGER" property="parentId"/>
    </resultMap>

    <insert id="insert" parameterType="data.dictionary.plateform.core.occupation.DataEntity"  >
     insert into t_data_occupation
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="dataName != null and dataName != ''">
                data_name,
            </if>
            <if test="dataLeft != null ">
                data_left,
            </if>
            <if test="dataRight != null ">
                data_right,
            </if>

            <if test="dataDepth != null ">
                data_depth,
            </if>

            <if test="parentId != null ">
                parent_id,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="dataName != null and dataName != ''">
                #{dataName,jdbcType=VARCHAR},
            </if>
            <if test="dataLeft != null">
                #{dataLeft,jdbcType=INTEGER},
            </if>
            <if test="dataRight != null">
                #{dataRight,jdbcType=INTEGER},
            </if>
            <if test="dataDepth != null">
                #{dataDepth,jdbcType=INTEGER},
            </if>
            <if test="parentId != null">
                #{parentId,jdbcType=INTEGER},
            </if>
        </trim>
    </insert>

    <update id="updateById" parameterType="data.dictionary.plateform.core.occupation.DataEntity">
        update t_data_occupation
        set

        <if test="dataName != null and dataName != ''">
            data_name = #{dataName,jdbcType=VARCHAR},
        </if>

        <if test="dataLeft != null and dataLeft !=0">
              data_left = #{dataLeft,jdbcType=INTEGER},
        </if>

        <if test="dataRight != null and dataRight !=0">
              data_right = #{dataRight,jdbcType=INTEGER},
        </if>

        <if test="dataDepth != null and dataDepth !=0">
              data_depth = #{dataDepth,jdbcType=INTEGER}
        </if>

        where id = #{id,jdbcType=INTEGER};
    </update>
    <select id="selectLinked" parameterType="java.lang.Integer" resultType="data.dictionary.plateform.core.occupation.DataEntity">
        SELECT
	          pare.*
        FROM
	        t_data_occupation AS node,
	        t_data_occupation AS pare
        WHERE
	        node.data_left BETWEEN pare.data_left AND pare.data_right
         AND node.id=#{id,jdbcType=INTEGER}
        ORDER BY pare.data_left
    </select>

    <insert id="insertForEach" parameterType="data.dictionary.plateform.core.occupation.DataEntity">
        insert into t_data_occupation
        (data_name,data_left,data_right,data_depth,parent_id)
        values
        <foreach collection="dataEntities" item="item" index="index" separator="," >
            (#{item.dataName,jdbcType=VARCHAR},
            #{item.dataLeft,jdbcType=INTEGER},
            #{item.dataRight,jdbcType=INTEGER},
            #{item.dataDepth,jdbcType=INTEGER},
            #{item.parentId,jdbcType=INTEGER})
        </foreach>
    </insert>


</mapper>

mySQL数据库结构

-- ----------------------------
-- Table structure for t_data_occupation
-- ----------------------------
DROP TABLE IF EXISTS `t_data_occupation`;
CREATE TABLE `t_data_occupation` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `data_name` varchar(64) DEFAULT NULL,
  `data_left` int(64) DEFAULT NULL,
  `data_right` int(64) DEFAULT NULL,
  `data_depth` int(64) DEFAULT NULL,
  `parent_id` int(64) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1061 DEFAULT CHARSET=utf8;


总结

这个接口输入是一个path,本地的excel文件地址:C:\Users\yongc\Desktop\data\1.xlsx
输出就是一个集合。
方法又简单又笨,但是这是一种比较可靠的解决方案,解决了有无的问题,虽然实施起来麻烦,但是一次操作就可以了,工程问题,能解决问题的就是好方法,暂且到这里了,以后有更好的方案会持续的放出来和大家一起交流。

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值