目录
前言
上次整理了单表的一些基本操作,现实工作可能会涉及复杂的多表查询,本文主要对Mybatis多表查询之一对多、多对一进行讲述。无论是一对多还是多对一,无非是涉及两个标签:collection和association,至于怎么去用和理解,后文会说明。每种查询都可以用两种方式去实现:按照查询嵌套处理(子查询)和按照结果嵌套处理(联表查询)。在进行操作前,需要把相关配置搞定,可以参考博主以往的博客。看之前需要对几个标签有所理解:
property:对象、成员变量属性的名称
column:数据库字段名称
javaType:对象属性的类型
ofType:集合中的泛型信息
association:一个复杂类型的关联
collection:一个复杂类型的集合
一、建立数据库
建立一个student表和teacher表,用外键将它们关联起来,一般公司出于性能和易维护性角度来说,不会使用外键去关联表,而是在业务层处理。
student表:
CREATE TABLE `student` ( `id` int(10) NOT NULL, `name` varchar(30) DEFAULT NULL, `tid` int(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, '姚伟', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, '谢帅阁', 3); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, '王小二', 2); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, '马小跳', 2); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, '巫妖王', 4); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (6, '范冰冰', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (7, '迪丽热巴', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (8, '李帅哥', 4); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (9, '老王哥', 1);
teacher表:
CREATE TABLE `teacher` ( `id` int(10) NOT NULL, `name` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `teacher` (`id`, `name`) VALUES (1, '秦老师'); INSERT INTO `teacher` (`id`, `name`) VALUES (2, '谢老师'); INSERT INTO `teacher` (`id`, `name`) VALUES (3, '尧老师'); INSERT INTO `teacher` (`id`, `name`) VALUES (4, '王老师');
二、多对一查询
何为多对一?假设有一个班的学生,某节课给他们上课的有一个固定的老师,你可以说多个学生关联这个老师。
2.1 编写实体类
Teacher.java:
package com.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Student { private int id; private String name; //学生需要关联一个老师 private Teacher teacher; }
Student.java:
package com.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class Teacher { private int id; private String name; }
2.2 编写接口
这里定义查询所有学生信息及对应老师信息的方法,有子查询和按结果嵌套两种方法,返回的是list集合类型。
StudentMapper.java:
package com.dao; import com.pojo.Student; import java.util.List; public interface StudentMapper { //子查询 List<Student> getStudent(); //按结果嵌套查询 List<Student> getStudent2(); }
2.3 编写Mapper配置文件 (StudentMapper.xml)
子查询:就是SQL里面的嵌套查询,我们现在有两张表,就需要查两次。由于是多个学生关联一个老师,所以这里用 association 标签,关联标签返回的是对象属性的类型(javaType)。我们首先要查询出所有的学生信息,因为学生实体类中除了变量属性还有对象属性(学生所关联的Teacher对象属性),变量属性可以很好地对应数据库的字段名,而对象属性需要用结果集映射。然后,根据查询出的学生tid,寻找对应的老师。因为映射之后,相应的tid已经有了,所以id=#{tid}括号里面的可以随意填写。
<mapper namespace="com.dao.StudentMapper"> <select id="getStudent" resultMap="StudentTeacher"> select * from mybatis.student; </select> <resultMap id="StudentTeacher" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="teacher"> select * from mybatis.teacher where id = #{tid} </select>
按结果嵌套查询:按结果嵌套查询就是SQL里面的联表查询,利用的是笛卡尔积将两张表关联,只需查一次,无论是效率方面还是理解层面上,按结果嵌套查询都要优于子查询,所以这里推荐使用第二种。可以很清楚地看出,按结果嵌套查询是先把查询完整语句给出,然后再对取别名的字段进行映射。对于对象属性的利用association进行字段对应。你可以这样理解,两张表都有name和id属性,如果不用association标签,两个name和id属性势必会冲突。
/
<select id="getStudent2" resultMap="StudentTeacher1"> select s.id sid, s.name sname, t.name tname,t.id tid from mybatis.teacher t, mybatis.student s where s.tid = t.id </select> <resultMap id="StudentTeacher1" type="Student"> <result property="name" column="sname"/> <result property="id" column="sid"/> <association property="teacher" javaType="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> </association> </resultMap>
2.4 多对一查询结果
@Test public void test() { SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> list = mapper.getStudent(); for (Student student : list) { System.out.println(student); } sqlSession.close(); }
三、一对多查询
何为一对多?即一个老师可以给多个学生上课,这些学生可以组成一个集合,所以表示为一对多,并且用collection标签来表示。
3.1 编写实体类
Teacher.java:
package com.pojo; import lombok.Data; import java.util.List; @Data public class Teacher { private int id; private String name; //一个老师拥有多个学生 private List<Student> students; }
Student.java:
package com.pojo; import lombok.Data; @Data public class Student { private int id; private String name; private int tid; }
3.2 编写接口
这里先是利用子查询和按结果嵌套查询去获取所有的老师及对应的学生,然后通过id获取指定老师下的所有学生及老师的信息。
TeacherMapper.java:
package com.dao; import com.pojo.Teacher; import java.util.List; public interface TeacherMapper { //获取所有的老师及对应的学生 List<Teacher> getTeacher(); List<Teacher> getTeacher2(); //获取指定老师下所有的学生及老师的信息 Teacher getTeacher3(int id); }
3.3 编写Mapper配置文件 (TeacherMapper.xml)
按结果嵌套查询:
<select id="getTeacher" resultMap="TeacherStudent"> select t.id tid, t.name tname, s.name sname, s.id sid from mybatis.teacher t, mybatis.student s where t.id = s.tid </select> <!--javaType=""指定属性的类型!集合中的泛型信息我们用ofType来获取--> <resultMap id="TeacherStudent" type="Teacher"> <result property="name" column="tname"/> <result property="id" column="tid"/> <collection property="students" ofType="Student"> <result property="name" column="sname"/> <result property="id" column="sid"/> <result property="tid" column="tid"/> </collection> </resultMap>
子查询:
<select id="getTeacher2" resultMap="TeacherStudent2"> select * from mybatis.teacher </select> <resultMap id="TeacherStudent2" type="Teacher"> <result column="id" property="id"/> <result column="name" property="name"/> <collection property="students" javaType="Arraylist" ofType="Student" select="getStudent" column="id"/> </resultMap> <select id="getStudent" resultType="Student"> select * from mybatis.student where tid = #{tid} </select>
按照指定id去查指定的老师,并得到这个老师下的所有学生(这里推荐使用按结果嵌套查询) :
<select id="getTeacher3" resultMap="TeacherStudent3"> select s.id sid, s.name sname, t.id tid, t.name tname from mybatis.student s, mybatis.teacher t where s.tid = t.id and t.id = #{id} </select> <resultMap id="TeacherStudent3" type="Teacher"> <result column="tid" property="id"/> <result column="tname" property="name"/> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>
3.4 一对多查询结果
package com; import com.dao.TeacherMapper; import com.pojo.Teacher; import com.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class MyTest { @Test public void test(){ SqlSession sqlSession= MybatisUtils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); List<Teacher> teacher = mapper.getTeacher(); for (Teacher teachers : teacher) { System.out.println(teachers); } sqlSession.close(); } @Test public void test1(){ SqlSession sqlSession= MybatisUtils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); List<Teacher> teacher = mapper.getTeacher2(); for (Teacher teachers : teacher) { System.out.println(teachers); } sqlSession.close(); } @Test public void test2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher3 = mapper.getTeacher3(4); System.out.println(teacher3); sqlSession.close(); } }
结果1:
结果2: