当查询数据分散在多个表中的时候,要一次将结果查出来,这时就需要进行表关联了,将关联之后的结果,映射到Java 对象上,并最终返回给请求端。本文一起来看一下基于Mybatis Plus从多个表中查询关联数据,并映射到结果集上的两种查询方法。
Mybatis Plus 关联查询和级联查询
两个表关联后,我们有两种返回查询结果集的方式,一种是平铺方式,也就是两个表的字段都放到一个entity中;另一种是在一个entity中持有另一个entity,这就是我们常说的级联查询。
我们举例以班级表和学生表为例,一个班级对应多个学生,一个学生对应一个班级,对应关系就是班级对学生是一对多,学生对班级是一对一(从一个学生的角度去查询,一个学生同一时间只能在一个班级)。
我们先来构造数据。
1、构造两个表,班级表和学生表
-- 构造班级表DROP TABLE IF EXISTS classz;CREATE TABLE classz(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '班级名称',PRIMARY KEY (id));DROP TABLE IF EXISTS student;CREATE TABLE student(id BIGINT(20) NOT NULL COMMENT '主键ID',cid BIGINT(20),name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',PRIMARY KEY (id));
2、初始化表中数据
DELETE FROM classz;INSERT INTO classz (id, name) VALUES(1, '一年一班'),(2, '一年二班');DELETE FROM student;INSERT INTO student (id, cid, name) VALUES(1, 1,'小明'),(2, 1,'小欧');
数据构造了两个班级,小明和小欧都在1班。
如果我们从学生的角度查询学生,并把学生对应的班级也关联出来,那么这两种方式的展示方式,我们先看第一种,平铺式的,也就是所有结果字段都在一个类中。
1、构造entity,用于保存结果
package com.itzhimei.mybatis.plus.model;import com.baomidou.mybatisplus.annotation.TableId;import lombok.Data;/** * @Auther: www.itzhimei.com * @Description: 班级和学生entity */@Datapublic class ClasszAndStudent { private Long id; private String name; private Long stdId; private Long cid; private String stdName;}
2、在mapper中构建一个查询方法,两个表关联,字段重名就命名别名,与entity对应上
package com.itzhimei.mybatis.plus.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.itzhimei.mybatis.plus.model.Classz;import com.itzhimei.mybatis.plus.model.ClasszAndStudent;import org.apache.ibatis.annotations.Select;import java.util.List;/** * @Auther: www.itzhimei.com * @Description: */public interface ClasszMapper extends BaseMapper { /** * 关联查询 * @return */ @Select("select a.*,b.id stdId,b.cid, b.name stdName from classz a ,student b where a.id = b.cid and a.id = 1") List selectClassAndStudents();}
3、测试
/** * @Auther: www.itzhimei.com * @Description: */@RunWith(SpringRunner.class)@SpringBootTestpublic class ClassStudentTest { @Autowired private ClasszMapper classzMapper; @Autowired private StudentMapper studentMapper; @Test public void testSelect() { List classzs = classzMapper.selectClassAndStudents(); classzs.forEach(System.out::println); }}
4、输出:
---------------SQL打印输出---------------==> Preparing: select a.*,b.id stdId,b.cid, b.name stdName from classz a ,student b where a.id = b.cid and a.id = 1 ==> Parameters: <== Columns: ID, NAME, STDID, CID, STDNAME<== Row: 1, 一年一班, 1, 1, 小明<== Row: 1, 一年一班, 2, 1, 小欧<== Total: 2-------------------代码打印输出----------------------ClasszAndStudent(id=1, name=一年一班, stdId=1, cid=1, stdName=小明)ClasszAndStudent(id=1, name=一年一班, stdId=2, cid=1, stdName=小欧)
我上面说到的平铺式的,就是这种效果:
ClasszAndStudent(id=1, name=一年一班, stdId=1, cid=1, stdName=小明)
另一种就是级联查询方式的返回查询结果。
1、构造实体,用于保存数据
package com.itzhimei.mybatis.plus.model;import com.baomidou.mybatisplus.annotation.TableField;import lombok.Data;import java.util.List;/** * @Auther: www.itzhimei.com * @Description: 学生entity */@Datapublic class Student { private Long id; private Long cid; private String name; @TableField(exist = false) private Classz classz;}
package com.itzhimei.mybatis.plus.model;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import lombok.Data;import java.util.List;/** * @Auther: www.itzhimei.com * @Description: 班级entity */@Datapublic class Classz { private Long id; private String name;}
2、在StudentMapper中添加一个级联查询方法
package com.itzhimei.mybatis.plus.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.itzhimei.mybatis.plus.model.Classz;import com.itzhimei.mybatis.plus.model.Student;import org.apache.ibatis.annotations.One;import org.apache.ibatis.annotations.Result;import org.apache.ibatis.annotations.Results;import org.apache.ibatis.annotations.Select;import java.util.List;/** * @Auther: www.itzhimei.com * @Description: */public interface StudentMapper extends BaseMapper { /** * 一对一的级联查询 * @return */ @Results(id="id",value = { @Result(property = "id",column = "id"), @Result(property = "name",column = "name"), @Result(property = "cid",column = "cid"), @Result(property = "classz",column = "cid", one = @One(select = "com.itzhimei.mybatis.plus.mapper.ClasszMapper.selectById")) }) @Select("select * from student ") List selectStudentsAndClass();}
注意代码中的注解,其中这一行:
@Result(property = "classz",column = "cid", one = @One(select = "com.itzhimei.mybatis.plus.mapper.ClasszMapper.selectById"))
作用就是执行了ClasszMapper.selectById()方法,用主键查询对应的班级,将查询结果set到了Student实体类中的Classz classz属性中(请看上面Student类中的属性)。
3、测试
package com.itzhimei.mybatis.plus.test;import com.itzhimei.mybatis.plus.mapper.ClasszMapper;import com.itzhimei.mybatis.plus.mapper.StudentMapper;import com.itzhimei.mybatis.plus.model.ClasszAndStudent;import com.itzhimei.mybatis.plus.model.Student;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 java.util.List;/** * @Auther: www.itzhimei.com * @Description: */@RunWith(SpringRunner.class)@SpringBootTestpublic class ClassStudentTest { @Autowired private ClasszMapper classzMapper; @Autowired private StudentMapper studentMapper; @Test public void testSelectCascade() { List students = studentMapper.selectStudentsAndClass(); students.forEach(System.out::println); }}
4、输出
---------------SQL打印输出---------------==> Preparing: select * from student ==> Parameters: <== Columns: ID, CID, NAME<== Row: 1, 1, 小明====> Preparing: SELECT id,name FROM classz WHERE id=? ====> Parameters: 1(Long)<==== Columns: ID, NAME<==== Row: 1, 一年一班<==== Total: 1<== Row: 2, 1, 小欧<== Total: 2-------------------代码打印输出----------------------Student(id=1, cid=1, name=小明, classz=Classz(id=1, name=一年一班))Student(id=2, cid=1, name=小欧, classz=Classz(id=1, name=一年一班))
看代码的输出结果:
Student(id=1, cid=1, name=小明, classz=Classz(id=1, name=一年一班))
Student中包含的Classz也带有数据,是这种Student(Classz)包含一个实体的效果,这就实现了一个一对一级联查询效果。