mybatis自关联一对多查询

自关联查询

自关联查询就是自己充当多方,同时也充当一方,即多和一都在同一张表中,一般这样的表其实可以看做是一个树形结构,在数据库表中有一个外键,该外键表示当前数据的父节点。下面以公司职位为例创建一张自关联的表。

建表语句

CREATE TABLE `t_employee` (
  `id` int(11) NOT NULL,
  `name` varchar(45) DEFAULT NULL,
  `job` varchar(20) DEFAULT NULL,
  `mgr` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

id是主键
name是员工姓名
job是工作岗位
mgr是上级领导的id
这里面mgr可以看做是外键引用的是上级领导的id,为了方便操作,这里先不建立外键关系。

初始化数据:

INSERT INTO `t_employee` VALUES (1001,'king','董事长',NULL),(1002,'jack','技术总监',1001),(1003,'paul','财务总监',1001),(1004,'tom','销售总监',1001),(1005,'tomas','技术一部经理',1002),(1006,'linda','技术二部经理',1002),(1007,'lucy','会计',1003),(1008,'lily','出纳',1003),(1009,'terry','销售一部经理',1004),(1010,'emma','销售二部经理',1004),(1011,'may','软件工程师',1005),(1012,'bella','软件工程师',1005),(1013,'kelly','软件工程师',1006);

一对多关联查询

创建javabean
当前类是一方,里面添加List属性用来表示多的一方

public class Employee {

    private int id;

    private String name;

    private String job;

    //表示多的一方,即当前员工的所有下属
    private List<Employee> children;

    //省略getter、setter和toString
}

创建dao

public interface EmployeeDao {

    List<Employee> selectChildrenByPid(int mgr);
}

创建mapper
创建EmployeeMapper.xml文件

<?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.monkey1024.dao.EmployeeDao">

    <!--形成递归查询-->
    <resultMap id="childrenMap" type="Employee">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <collection property="children" ofType="Employee" select="selectChildrenByPid" column="id"/>
    </resultMap>

    <select id="selectChildrenByPid" resultMap="childrenMap">
        SELECT id, name ,job
        FROM t_employee
        WHERE mgr=#{pid}
    </select>
</mapper>

在上面的sql语句中,实际上形成了递归查询,resultMap中的collection里面,select属性表示会继续执行selectChildrenByPid这个sql语句,column表示将id作为属性传入sql中,此处是pid。有一点需要注意的是column中的id要跟sql语句中的id一致,因为本次查询出的id会作为条件pid再次传入sql中执行。

定义测试类

package com.monkey1024.test;

import com.monkey1024.bean.Employee;
import com.monkey1024.dao.EmployeeDao;
import com.monkey1024.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class EmployeeTest01 {

    private SqlSession sqlSession;

    private EmployeeDao employeeDao;

    /**
     * 测试时先执行该方法创建EmployeeDao对象
     */
    @Before
    public void initStudentDao(){
        sqlSession = MyBatisUtil.getSqlSession();
        //通过该方法可以获取TeamDao的对象
        employeeDao = sqlSession.getMapper(EmployeeDao.class);
    }

    /**
     * 执行完成后需要关闭sqlSession
     */
    @After
    public void closeSession() {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void selectChildrenByPid(){
        List<Employee> employeeList = employeeDao.selectChildrenByPid(1002);

        employeeList.forEach((e)->{
            System.out.println(e);
        });
    }
}

查询结果包含指定员工和其下属员工
上面的执行结果中可以看到会将id是1002这位员工的所有下属打印出来,但是不包含1002这位员工的信息,如果想要查询的结果包含1002这位员工的信息需要在mapper中添加下面内容:

<select id="selectEmployeeByPid" resultMap="childrenMap">
    SELECT id, name ,job
    FROM t_employee
    WHERE id=#{id}
</select>

上面sql就是根据id查询员工数据,但是这里面的resultMap指定的是childrenMap,所以它会继续进行递归查询将其下属也全部查询出来,这样子在查询结果中就包含了当前员工的信息了。

在dao中添加下面方法

List<Employee> selectEmployeeByPid(int mgr);

添加测试方法

@Test
public void selectEmployeeByPid(){
    List<Employee> employeeList = employeeDao.selectEmployeeByPid(1002);

    employeeList.forEach((e)->{
        System.out.println(e);
    });
}