在工作中,如果持久层框架使用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
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常见一对一、一对多关系映射的多种实现方式,大家可以根据自己的需求使用最合适的方式。本文仅仅是笔者的一些总结和使用经验总结,如有不对之处,希望大家指点一二,希望能对大家有所帮助。