Mybatis工作中常见关系映射总结

一、简介

在工作中,如果持久层框架使用mybatis的话,基本上都会涉及到关联关系映射处理,本文将对Mybatis中的一对多、一对一、递归查询树结构数据等做一个比较系统的总结,加深自己对Mybatis高级映射的理解。下面我们通过几个示例详细说明用法。

 

二、准备工作

在进行创建项目之前,我们首先要准备几张表,tbl_class(班级表)、tbl_teacher(教师表)、tbl_student(学生表)、tbl_item(测评项表)。具体的sql如下:

/*
SQLyog Ultimate v11.24 (32 bit)
MySQL - 5.5.44 : Database - springboot-mybatis
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`springboot-mybatis` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `springboot-mybatis`;

/*Table structure for table `tbl_class` */

DROP TABLE IF EXISTS `tbl_class`;

CREATE TABLE `tbl_class` (
  `c_id` int(11) NOT NULL AUTO_INCREMENT,
  `c_name` varchar(20) DEFAULT NULL,
  `teacher_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`c_id`),
  KEY `fk_teacher_id` (`teacher_id`),
  CONSTRAINT `fk_teacher_id` FOREIGN KEY (`teacher_id`) REFERENCES `tbl_teacher` (`t_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Data for the table `tbl_class` */

insert  into `tbl_class`(`c_id`,`c_name`,`teacher_id`) values (1,'科技一班',1),(2,'科技二班',2),(3,'科技三班',3);

/*Table structure for table `tbl_item` */

DROP TABLE IF EXISTS `tbl_item`;

CREATE TABLE `tbl_item` (
  `item_id` int(11) NOT NULL AUTO_INCREMENT,
  `item_name` varchar(20) DEFAULT NULL,
  `parent_item_id` int(11) DEFAULT NULL COMMENT '父ID',
  `zt` int(11) DEFAULT NULL,
  PRIMARY KEY (`item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;

/*Data for the table `tbl_item` */

insert  into `tbl_item`(`item_id`,`item_name`,`parent_item_id`,`zt`) values (1,'测评项1',NULL,1),(2,'测评项1-1',1,1),(3,'测评项1-2',1,1),(4,'测评项1-3',1,1),(5,'测评项1-2-1',3,1),(6,'测评项1-2-2',3,1),(7,'测评项1-3-1',4,1),(8,'测评项1-3-2',4,1),(9,'测评项2',NULL,1),(10,'测评项2-1',9,1),(11,'测评项2-2',9,1),(12,'测评项2-1-1',10,1),(13,'测评项2-1-2',10,NULL);

/*Table structure for table `tbl_student` */

DROP TABLE IF EXISTS `tbl_student`;

CREATE TABLE `tbl_student` (
  `s_id` int(11) NOT NULL AUTO_INCREMENT,
  `s_name` varchar(20) DEFAULT NULL,
  `class_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`s_id`),
  KEY `fk_class_id` (`class_id`),
  CONSTRAINT `fk_class_id` FOREIGN KEY (`class_id`) REFERENCES `tbl_class` (`c_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

/*Data for the table `tbl_student` */

insert  into `tbl_student`(`s_id`,`s_name`,`class_id`) values (1,'学生1',1),(2,'学生2',1),(3,'学生3',2),(4,'学生4',2),(5,'学生5',2),(6,'学生6',3);

/*Table structure for table `tbl_teacher` */

DROP TABLE IF EXISTS `tbl_teacher`;

CREATE TABLE `tbl_teacher` (
  `t_id` int(11) NOT NULL AUTO_INCREMENT,
  `t_name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`t_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Data for the table `tbl_teacher` */

insert  into `tbl_teacher`(`t_id`,`t_name`) values (1,'张三'),(2,'李四'),(3,'王五');

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

 

三、一对一

在mybatis中要实现一对一关联查询,主要是通过标签来实现。

 

【案例】查询所有的班级以及关联的教师信息(假设一个教师只带一个班级)

【a】首先,创建教师实体类Teacher.java:

package com.wsh.springboot.springbootmybatis.entity;

/**
 * @Description: 教师实体类
 * @Author: weishihuai
 * @Date: 2019/3/31 08:56
 */
public class Teacher {
    /**
     * 教师编号
     */
    private Integer tid;
    /**
     * 教师姓名
     */
    private String tname;

    public Teacher() {
    }

    public Teacher(String tname) {
        this.tname = tname;
    }

    public Integer getTid() {
        return tid;
    }

    public void setTid(Integer tid) {
        this.tid = tid;
    }

    public String getTname() {
        return tname;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "tid=" + tid +
                ", tname='" + tname + '\'' +
                '}';
    }

    public void setTname(String tname) {
        this.tname = tname;

    }

}

【b】创建班级实体类ClassRoom.java:

/**
 * @Description: 班级实体类
 * @Author: weishihuai
 * @Date: 2019/3/31 09:06
 */
public class ClassRoom {

    /**
     * 班级ID
     */
    private Integer cid;
    /**
     * 班级名称
     */
    private String cname;
    /**
     * 班级的教师信息(假设一个老师只带一个班级)
     */
    private Teacher teacher;

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public String toString() {
        return "ClassRoom{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}

注意,在ClassRoom中加入了教师实体属性teacher.,下面我们讲解三种一对一关系映射的方法:

【c】定义mapper查询接口:

/**
 * @Description: Mapper接口
 * @Author: weishihuai
 * @Date: 2019/3/31 09:11
 */
@Repository
public interface ClassRoomMapper {

    /**
     * 查询所有的班级以及关联的教师信息(嵌套结果方式)
     *
     * @return 班级以及关联的教师信息
     */
    List getAllClassRoomAndTeacherInfo01();

    /**
     * 查询所有的班级以及关联的教师信息(嵌套查询方式)
     *
     * @return 班级以及关联的教师信息
     */
    List getAllClassRoomAndTeacherInfo02();

    /**
     * 查询所有的班级以及关联的教师信息(拓展VO方式)
     *
     * @return 班级以及关联的教师信息
     */
    List getAllClassRoomAndTeacherInfo03();

}

【d】定义mapper.xml文件:

第一种方法:嵌套结果方式



    
    
        
        
        
        
        
            
            
        
    

    

 

第二种方法:分步查询方式

 
    
        
        
        
        
    

    

    

第三种方式:拓展VO方式

这种方式需要定义个包含教师类的属性以及班级类的属性:

/**
 * @Description: 班级信息拓展VO
 * @Author: weishihuai
 * @Date: 2019/3/31 10:52
 */
public class ClassRoomTzVO {
    /**
     * 班级ID
     */
    private Integer cid;
    /**
     * 班级名称
     */
    private String cname;
    /**
     * 教师ID
     */
    private Integer tid;
    /**
     * 教师信息
     */
    private String tname;

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    public Integer getTid() {
        return tid;
    }

    public void setTid(Integer tid) {
        this.tid = tid;
    }

    public String getTname() {
        return tname;
    }

    public void setTname(String tname) {
        this.tname = tname;
    }

    @Override
    public String toString() {
        return "ClassRoomTzVO{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                ", tid=" + tid +
                ", tname='" + tname + '\'' +
                '}';
    }
}
 
    

【e】测试:

 //测试 一对一关系映射  第一种方式
    @Test
    public void testGetAllClassRoomAndTeacherInfo01() {
        List allClassRoomAndTeacherInfo = classRoomMapper.getAllClassRoomAndTeacherInfo01();
        for (ClassRoom classRoom : allClassRoomAndTeacherInfo) {
            System.out.println(classRoom);
        }
    }

    //测试 一对一关系映射  第二种方式
    @Test
    public void testGetAllClassRoomAndTeacherInfo02() {
        List allClassRoomAndTeacherInfo = classRoomMapper.getAllClassRoomAndTeacherInfo02();
        for (ClassRoom classRoom : allClassRoomAndTeacherInfo) {
            System.out.println(classRoom);
        }
    }

    //测试 一对一关系映射  第三种方式
    @Test
    public void testGetAllClassRoomAndTeacherInfo03() {
        List allClassRoomAndTeacherInfo = classRoomMapper.getAllClassRoomAndTeacherInfo03();
        for (ClassRoomTzVO classRoomTzVO : allClassRoomAndTeacherInfo) {
            System.out.println(classRoomTzVO);
        }
    }

【f】查询结果:

运行junit测试,发现三种方式的查询结果都是一致的,具体项目中可以根据自己的喜好选择合适的方法。

 

四、一对多

mybatis中实现一对多关系映射,主要是通过标签来实现。

【案例】: 查询所有的班级以及班级的所有学生信息、教师信息

【a】定义Student.java:

/**
 * @Description: 学生实体类
 * @Author: weishihuai
 * @Date: 2019/3/31 20:51
 */
public class Student {
    /**
     * 学生ID
     */
    private Integer sid;
    /**
     * 学生姓名
     */
    private String sname;

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }


    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                '}';
    }
}

【b】在ClassRoom类中加入 private List students;

package com.wsh.springboot.springbootmybatis.entity;

import java.util.List;

/**
 * @Description: 班级实体类
 * @Author: weishihuai
 * @Date: 2019/3/31 09:06
 */
public class ClassRoom {

    /**
     * 班级ID
     */
    private Integer cid;
    /**
     * 班级名称
     */
    private String cname;
    /**
     * 班级的教师信息(假设一个老师只带一个班级)
     */
    private Teacher teacher;
    /**
     * 学生集合
     */
    private List students;

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public List getStudents() {
        return students;
    }

    public void setStudents(List students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "ClassRoom{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                ", teacher=" + teacher +
                ", students=" + students +
                '}';
    }
}

【c】编写mapper.java接口

 /**
     * 查询所有班级的所有学生(嵌套结果方式)
     *
     * @return 班级所有学生信息
     */
    List getAllStudents01();

    /**
     * 查询所有班级的所有学生(分步查询方式)
     *
     * @return 班级所有学生信息
     */
    List getAllStudents02();

【d】定义mapper.xml映射文件:下面也是讲解两种实现方法。

第一种方法:嵌套结果方式



    
    
        
        
        
        
            
            
        
        
        
        
            
            
        
    

    

第二种方法:分步查询方式


    

    

    
        
        
        
        
        
    

    

【e】测试用例

 //测试 一对多关系映射  第一种方式
    @Test
    public void testGetAllStudents01() {
        List allStudents = classRoomMapper.getAllStudents01();
        for (ClassRoom classRoom : allStudents) {
            System.out.println(classRoom);
        }
    }

    //测试 一对多关系映射  第二种方式
    @Test
    public void testGetAllStudents02() {
        List allStudents = classRoomMapper.getAllStudents02();
        for (ClassRoom classRoom : allStudents) {
            System.out.println(classRoom);
        }
    }

【f】查询结果:

可见,以上两种方式都能实现一对多关系映射。

 

五、递归一对多

【案例】递归查找测评项信息以及子测评项信息(树结构数据)

【a】创建测评项实体类Item.java:

package com.wsh.springboot.springbootmybatis.entity;

import java.util.List;

/**
 * @Description: 测评项实体类
 * @author: weishihuai
 * @Date: 2019/4/3 16:15
 */
public class Item {

    private Integer itemId;
    private String itemName;
    private Integer zt;
    private List children;

    public List getChildren() {
        return children;
    }

    public void setChildren(List children) {
        this.children = children;
    }

    public Integer getItemId() {
        return itemId;
    }

    public void setItemId(Integer itemId) {
        this.itemId = itemId;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public Integer getZt() {
        return zt;
    }

    public void setZt(Integer zt) {
        this.zt = zt;
    }

    @Override
    public String toString() {
        return "Item{" +
                "itemId=" + itemId +
                ", itemName='" + itemName + '\'' +
                ", zt=" + zt +
                ", children=" + children +
                '}';
    }
}

【b】编写mapper.java接口

 /**
     * 递归查找测评项以及测评项下面的子测评项
     *
     * @param id 测评项ID
     * @return 测评项以及测评项下面的子测评项
     */
    List getAllItemsAndChildItems(@Param("id") Integer id);

【c】编写mapper.xml映射文件

 

    
    

    
        
        
        
        
        
    

    
    
    
    

【d】测试用例

 //测试 递归查找测评项信息以及子测评项信息(树结构数据)
    @Test
    public void testGetAllItemsAndChildItems() {
        List items = classRoomMapper.getAllItemsAndChildItems(null);
        System.out.println(JSON.toJSONString(items));
        for (Item item : items) {
            System.out.println(item.getItemName());
            System.out.println(item.getChildren());
        }
    }

【e】查询结果

[
    {
        "children":[
            {
                "children":[

                ],
                "itemId":2,
                "itemName":"测评项1-1",
                "zt":1
            },
            {
                "children":[
                    {
                        "children":[

                        ],
                        "itemId":5,
                        "itemName":"测评项1-2-1",
                        "zt":1
                    },
                    {
                        "children":[

                        ],
                        "itemId":6,
                        "itemName":"测评项1-2-2",
                        "zt":1
                    }
                ],
                "itemId":3,
                "itemName":"测评项1-2",
                "zt":1
            },
            {
                "children":[
                    {
                        "children":[

                        ],
                        "itemId":7,
                        "itemName":"测评项1-3-1",
                        "zt":1
                    },
                    {
                        "children":[

                        ],
                        "itemId":8,
                        "itemName":"测评项1-3-2",
                        "zt":1
                    }
                ],
                "itemId":4,
                "itemName":"测评项1-3",
                "zt":1
            }
        ],
        "itemId":1,
        "itemName":"测评项1",
        "zt":1
    },
    {
        "children":[
            {
                "children":[
                    {
                        "children":[

                        ],
                        "itemId":12,
                        "itemName":"测评项2-1-1",
                        "zt":1
                    },
                    {
                        "children":[

                        ],
                        "itemId":13,
                        "itemName":"测评项2-1-2"
                    }
                ],
                "itemId":10,
                "itemName":"测评项2-1",
                "zt":1
            },
            {
                "children":[

                ],
                "itemId":11,
                "itemName":"测评项2-2",
                "zt":1
            }
        ],
        "itemId":9,
        "itemName":"测评项2",
        "zt":1
    }
]

这种数据结构常见于树形结构展示数据的时候用到, 可以使用json查看器观察其数据结构,大家在项目中有这样的需求可以使用这种方式实现,比较方便快捷。

文中对一些需要注意的地方已经写了一些注释,如果大家需要源码的话,可以到这里 https://gitee.com/weixiaohuai/springboot-mybatis.git去下载参考一下。

 

六、总结

以上就是关于mybatis常见一对一、一对多关系映射的多种实现方式,大家可以根据自己的需求使用最合适的方式。本文仅仅是笔者的一些总结和使用经验总结,如有不对之处,希望大家指点一二,希望能对大家有所帮助。

 

你可能感兴趣的:(Mybatis)