MyBatis级联(关联)查询详解(包括分步、按需查询等)
文章目录一、环境搭建项目结构数据库1.学生表com_student2.课程表com_course3.班级表com_class4.档案表com_archives二、MyBatis级联查询(一对一)三、MyBatis级联查询(一对多)1.集合查询2.分步查询四、MyBatis级联查询(多对多)一、pandas是什么?二、使用步骤1.引入库2.读入数据总结一、环境搭建项目结构数据库1.学生表com_st
·
文章目录
一、环境搭建
项目结构

数据库
1.学生表com_student

2.课程表com_course

3.班级表com_class

4.档案表com_archives

关系说明:
- 学生表(com_student)和学生档案表(com_archives)的关系是一对一,即一名学生只有一个档案,一个档案只能代表一名学生。
- 学生表(com_student)和学生班级表(com_class)的关系是一对多,即一名学生只有一个班级,一个班级有众多学生。
- 学生表(com_student)和学生课程表(com_course)的关系是多对多,即一名学生有多门课程,一门课程有多名学生选修。
无论是哪一种查询,真正的关注点只有映射配置文件一个(即:StudentDao.xml)。
二、MyBatis级联查询(一对一)
1.association、级联属性查询
说明:档案类(Archives.java)是学生类(Student.java)的一个属性;查询结束返回的是一个对象。
使用场景:查询出某一个学生的所有信息并且显示出该学生的档案信息。
- Student.java(实体类)
package com.xx.Bean;
public class Student {
private int id;
private String stuName;
private int stuAge;
//课程表的外键
private int stuClassId;
//Archives类是Student类的一个属性(级联查询)
private Archives arch;
public Archives getArch() {
return arch;
}
public void setArch(Archives arch) {
this.arch = arch;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public int getStuClassId() {
return stuClassId;
}
public void setStuClassId(int stuClassId) {
this.stuClassId = stuClassId;
}
public Student(int id, String stuName, int stuAge, int stuClassId, Archives arch) {
super();
this.id = id;
this.stuName = stuName;
this.stuAge = stuAge;
this.stuClassId = stuClassId;
this.arch = arch;
}
@Override
public String toString() {
return "Student [id=" + id + ", stuName=" + stuName + ", stuAge=" + stuAge + ", stuClassId=" + stuClassId
+ ", arch=" + arch + "]";
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
}
- Archives.java(实体类)
package com.xx.Bean;
public class Archives {
private int id;
private String archName;
private String archNum;
//学生表的外键
private int stuId;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getArchName() {
return archName;
}
public void setArchName(String archName) {
this.archName = archName;
}
public String getArchNum() {
return archNum;
}
public void setArchNum(String archNum) {
this.archNum = archNum;
}
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
@Override
public String toString() {
return "Archives [id=" + id + ", archName=" + archName + ", archNum=" + archNum + ", stuId=" + stuId + "]";
}
public Archives(int id, String archName, String archNum, int stuId) {
super();
this.id = id;
this.archName = archName;
this.archNum = archNum;
this.stuId = stuId;
}
public Archives() {
super();
// TODO Auto-generated constructor stub
}
}
- StudentDao.java(接口)
package com.xx.Dao;
import java.util.List;
import com.xx.Bean.Student;
public interface StudentDao {
//传递一个学生id,返回一条Student数据
public Student getStudent(int id);
}
- mybatis-config.xml(全局配置)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- resource是jdbc配置文件 -->
<properties resource="sql.properties"></properties>
<!--
mapUnderscoreToCamelCase:是否开启自动驼峰命名规则(camel case)映射
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${sql_driver}"/>
<property name="url" value="${sql_url}"/>
<property name="username" value="${sql_username}"/>
<property name="password" value="${sql_password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.xx.Dao"/>
</mappers>
</configuration>
- StudentDao.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.xx.Dao.StudentDao">
<select id="getStudent" resultMap="getStudentForAssoc">
select
stu.*,
arch.id aid,
arch.arch_name,
arch.arch_num,
arch.stu_id
from
com_student stu
left join
com_archives arch
on
stu.id = arch.stu_id
where
stu.id=#{id}
</select>
<!-- 方法一:了解 -->
<resultMap type="com.xx.Bean.Student" id="getStudentForSimple">
<id property="id" column="id"/>
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
<result property="stuClassId" column="stu_class_id"/>
<result property="arch.id" column="aid"/>
<result property="arch.archName" column="arch_name"/>
<result property="arch.archNum" column="arch_num"/>
<result property="arch.stuId" column="stu_id"/>
</resultMap>
<!-- 方法二:使用此种方法!!! -->
<resultMap type="com.xx.Bean.Student" id="getStudentForAssoc">
<!--
property:类的属性,
column:查询出来的字段名。
通过resultMap将MyBaits查询出来的数据和实体类的字段名一一对应。
-->
<id property="id" column="id"/>
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
<result property="stuClassId" column="stu_class_id"/>
<!--
association表示将两个对象联合起来,使MyBatis能够知道谁是谁的对象,谁该怎样封装谁!!!
property:表示实体类(Student)下边的哪一个属性。
javaType:表示实体类(Student)下边的arch属性要封装com.xx.Bean.Archives类。
-->
<association property="arch" javaType="com.xx.Bean.Archives">
<result property="id" column="aid"/>
<result property="archName" column="arch_name"/>
<result property="archNum" column="arch_num"/>
<result property="stuId" column="stu_id"/>
</association>
</resultMap>
</mapper>
执行顺序:
- MyBatis首先执行select语句,从数据库中查询出如下结果:
- 然后MyBatis准备返回数据,默认是按照resultType返回;但是此处找到了resultMap自定义的规则,于是对照resultMap中的唯一标志getStudentForAssoc名去找对应的规则。
- 找到对应resultMap规则后,按照规则提示一个对一个的封装!!!
- 总结:自定义规则的时候,只需记住property代表要封装的实体类属性,column代表数据库中已经查出来的数据字段,我们要将数据字段封装进实体类属性当中!
- sql.properties(数据库连接池)
sql_username=root//自己数据库链接名
sql_password=root//登录数据库用户密码
sql_driver=com.mysql.jdbc.Driver
sql_url=jdbc:mysql://localhost:3306/mybatistest//要使用的数据库
- test.java(JUnit测试)
package com.xx.JUnit;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import com.xx.Bean.Student;
import com.xx.Dao.StudentDao;
public class test {
private String resource;
private InputStream inputStream;
private SqlSessionFactory sqlSessionFactory;
private SqlSession session;
private Student stu;
@Before
public void initMybatis() throws IOException {
resource = "mybatis-config.xml";
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void getStudent() {
session = sqlSessionFactory.openSession();
StudentDao dao = session.getMapper(StudentDao.class);
System.out.println(dao.getStudent(1));
//关闭和数据库的链接
session.close();
}
}
//双击getStudent选中,右击->run As->JUnit Test运行。
运行结果:
2.association分步查询
使用场景同“集合查询”一样,不同的是,使用集合级联查询的方式书写sql语句比较复杂。分步查询符合个人所能想到的最简单的思维逻辑; 即:我先按照id查询出这个学生的详细来,然后在学生的详细信息中找到该学生的档案编号,再根据这个档案表好去查询学生的详细档案信息。- StudentDao.java(学生接口类)
//新加分步查询方法
public Student spiltGetStudent(int id);
- ArchivesDao.java(档案接口类)
//按照id查询档案信息
//首先执行的是查询学生信息的mapper映射语句,然后想办法将查询出来的id传递到此处。
public Archives getArchivesByid(int id);
- ArchivesDao.xml(按照id查询档案)
<?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.xx.Dao.ArchivesDao">
<!-- 查询档案,返回的是一个档案实体类 -->
<select id="getArchivesByid" resultType="com.xx.Bean.Archives">
select * from com_archives where id=#{id}
</select>
</mapper>
- StudentDao.xml(按照id查询学生)
<!-- 按照id查询出学生信息,然后执行自定义的resultMap规则 -->
<select id="spiltGetStudent" resultMap="spiltGetStudentForAssc">
select * from com_student where id=#{id}
</select>
<!-- -->
<resultMap type="com.xx.Bean.Student" id="spiltGetStudentForAssc">
<id property="id" column="id"/>
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
<result property="stuClassId" column="stu_class_id"/>
<!--
逻辑:我查询出学生的信息了,接下来应该告诉mybais怎样根据当前信息去查询档案数据库。
select:指定一个查询sql的唯一标识(就是告诉MyBatis接下来去使用select指定的这个查询),其余的封装操作由MyBatis完成。
column:要传入的条件(此处是id,和select指定的查询语句对应)。
注意:select尽量对应全类名。
-->
<association
property="arch"
select="com.xx.Dao.ArchivesDao.getArchivesByid"
column="id">
</association>
</resultMap>
- JUnit
@Test
public void splitGetClassStudent() {
//分步查询
session = sqlSessionFactory.openSession();
StudentDao dao = session.getMapper(StudentDao.class);
System.out.println(dao.spiltGetStudent(3));
session.close();
}

- 查询结果如图片
- 说明:cong图中的日志打印可以看到,数据库被请求查询了多次,最终得出了结果。
- 问题:若我只是想根据学生id得出该学生的档案编号这一条数据,但使用分步查询每次都要请求多次数据库,十分浪费资源,此时就用到按需查询。
3.按需查询
在mybatis-config.xml中加入两条设置:
<!--
mapUnderscoreToCamelCase:是否开启自动驼峰命名规则(camel case)映射。
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。
aggressiveLazyLoading:开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/> //加这里
<setting name="aggressiveLazyLoading" value="false"/> //加这里
</settings>
再次执行如下测试时,输出结果发生改变
结果:
只请求了一次数据库便得出结果。
按需加载就是需要的时候去查询,延迟加载就是不着急加载(不着急查询)。
- 补充:在assoication标签中有一个fetchType属性,该属性有两个属性:lazy和eager。前者为延迟加载,后者是立即加载。
- 该属性中的eager可以用来关闭全局配置文件定义的延迟加载(只是关闭当前查询的延迟加载)。
- 即当写fetchType=eager时,MyBatis对数据库的请求是多次请求,由延迟加载变为立即加载(立即请求数据库)。结果如下图二所示:
三、MyBatis级联查询(一对多)
1.集合查询
说明:班级类(Class.java)中有一个是学生类(Student.java)的列表属性,保存了这个班级中的所有学生,查询结束返回的是一个集合。
使用场景:查询某一个班级的具体信息并且返回该班级下的所有学生信息。
- Class.java(实体类)
package com.xx.Bean;
import java.util.List;
public class Class {
private int id;
private String className;
private String classNum;
//列表属性,用来保存本班级下面的所有的学生。
private List<Student> students;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getClassNum() {
return classNum;
}
public void setClassNum(String classNum) {
this.classNum = classNum;
}
//可以通过此方法来单独取得所有的学生的属性,不取班级属性。
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "Class [id=" + id + ", className=" + className + ", classNum=" + classNum + ", students=" + students
+ "]";
}
public Class(int id, String className, String classNum, List<Student> students) {
super();
this.id = id;
this.className = className;
this.classNum = classNum;
this.students = students;
}
public Class() {
super();
// TODO Auto-generated constructor stub
}
}
- ClassDao.java(接口)
在ClassDao.java接口类中写入如下方法。
package com.xx.Dao;
import java.util.List;
public interface ClassDao {
//传入班级的id,返回本班级的基本信息以及本班级下面的所有的学生的信息->collection
public List<Class> getClassStudent(int id);
//传入班级的id,返回本班级的基本信息以及本班级下面的所有的学生的信息->分步查询。
public List<Class> splitGetClassStudent(int id);
}
- ClasstDao.xml(映射配置文件)
在ClassDao.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">
<!-- namespace是指要处理哪一个接口类下的方法,所以必须要改为ClassDao -->
<mapper namespace="com.xx.Dao.ClassDao">
<!-- 先去数据库中执行,成功执行之后再去写resultMap -->
<select id="getClassStudent" resultMap="classStudents">
select
c.*,
stu.id sid,
stu.stu_name,
stu.stu_age,
stu.stu_class_id
from
com_class c
left join
com_student stu
on
c.id = stu.stu_class_id
where
c.id=#{id};
</select>
<!-- type是对班级类定义的封装规则,该规则的唯一标识是classStudents -->
<resultMap type="com.xx.Bean.Class" id="classStudents">
<!--
collection:定义集合元素的封装
property:指定实体类的哪一个属性是集合属性,MyBatis会将查询出来的集合封装到这里。
javaType:指定对象的类型,和ofType区分。
ofType:指定集合里面元素的类型。
-->
<id property="id" column="id"/>
<result property="className" column="class_name"/>
<result property="classNum" column="class_num"/>
<collection property="students" ofType="com.xx.Bean.Student">
<result property="id" column="sid"/>
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
<result property="stuClassId" column="stu_class_id"/>
</collection>
</resultMap>
</mapper>
- JUnit测试
@Test
public void getClassStudent() {
session = sqlSessionFactory.openSession();
StudentDao dao = session.getMapper(StudentDao.class);
List<Class> list = dao.getClassStudent(2);
System.out.println(list);
session.close();
}
打印结果如下:
2.collection分步查询
本例在association分步查询中已经开启过按需加载和延迟加载。
- ClassDao.java
//此处出现过一个错误,当返回值是Class的时候,一直报错。即public Class 。。。
//原因估计是因为Class是一个关键字,下次起名不能这么随意。
public List<Class> splitGetClassStudent(int id);
- StudentDao.java
//使用collection的分步查询
//根据传递过来的班级ID查询出该班级所有的学生信息
public List<Student> getStudentListByClassId(int id);
- ClassDao.xml
<select id="splitGetClassStudent" resultMap="splitGetClassStudentMap">
select * from com_class where id=#{id}
</select>
<resultMap type="com.xx.Bean.Class" id="splitGetClassStudentMap">
<id property="id" column="id"/>
<result property="className" column="class_name"/>
<result property="classNum" column="class_num"/>
<collection
property="students"
select="com.xx.Dao.StudentDao.getStudentListByClassId"
column="id">
</collection>
</resultMap>
- StudentDao.xml
<!-- resultType是集合里面元素的类型 -->
<!-- 根据传递过来的班级ID查询出该班级所有的学生信息 -->
<select id="getStudentListByClassId" resultType="com.xx.Bean.Student">
select * from com_student where stu_class_id=#{id}
</select>
- JUnit
@Test
public void getStudentListById() {
//使用collection的分步查询方法
session = sqlSessionFactory.openSession();
ClassDao dao = session.getMapper(ClassDao.class);
System.out.println("------------------------------------");
System.out.println(dao.splitGetClassStudent(2));
session.close();
}
打印结果:
四、MyBatis级联查询(多对多)
数据库方面加入一个中间表,实体类方面使用两个一对多查询就好。
更多推荐










所有评论(0)