Code Ease Code Ease
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档

神秘的鱼仔

你会累是因为你在走上坡路
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档
服务器
  • Java核心基础

  • 框架的艺术

    • Spring

    • Mybatis

      • Mybatis的原理介绍及超详细使用
      • Mybatis增删改查,这一篇足以
      • Mybatis配置详细解析
      • MyBatis实现复杂环境的Sql查询
        • (一)resultMap结果映射
        • (二)复杂环境下的resultMap使用
          • 2.1 多对一查询(association)
          • 2.2 一对多查询(collection)
        • (三)总结
      • Mybatis日志原来是这样使用的
      • Mybatis动态Sql详解
      • Mybatis缓存详解
      • 想在一个项目中实现多数据源切换?几行代码就搞定了
      • 用了MybatisPlus后,我很久没有手写sql了
      • 用了MyBatis的项目 如何优雅地打印SQL
    • SpringBoot

    • MQ

    • Zookeeper

    • netty

  • 分布式与微服务

  • 开发经验大全

  • 版本新特性

  • Java
  • 框架的艺术
  • Mybatis
CodeEase
2023-09-20
目录

MyBatis实现复杂环境的Sql查询

作者:鱼仔
博客首页: codeease.top (opens new window)
公众号:Java鱼仔

在前面的学习中,我们差不多把Mybatis的基本增删改查、配置文件的配置都讲解了一遍,但是在实际的开发中我们编写的sql不会那么简单,今天就来模拟复杂环境的Sql查询

# (一)resultMap结果映射

resultMap 元素是 MyBatis 中最重要最强大的元素,我们之前所写的sql语句,返回值都是简单的基本数据类型或者某一个实体类,比如下面这段sql返回的就是最简单的User类型。

<select id="getUserById" resultType="com.javayz.pojo.User" parameterType="int">
    select * from user where id=#{id};
</select>
1
2
3

现在思考一下下面这种情况,如果实体类中定义的某一个字段和数据库中的字段不一致怎么办

public class User {
    private int id;
    private String lastname;
    //.....
}
1
2
3
4
5

比如我定义了一个User类,包含id和lastname两个属性,而数据库中这两个字段的名字为id和name。此时再执行查询时结果如下:lastname这个字段直接为null

4-1.png

这时候我们就可以使用resultMap来解决这个问题,resultMap可以讲数据库中的字段映射到实体类上。column代表数据库中的字段名,properties代表实体类中的字段名,通过映射之后Mybatis就可以找到对应的字段。

<resultMap id="UserMap" type="User">
    <!--column代表数据库中的字段名,properties代表实体类中的字段名-->
    <result column="id" property="id"/>
    <result column="name" property="lastname"/>
</resultMap>

<select id="getUserById" resultMap="UserMap" parameterType="int">
    select id,name from user where id=#{id};
</select>
1
2
3
4
5
6
7
8
9

ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

# (二)复杂环境下的resultMap使用

在实际的应用场景中,我们会遇到很多比较复杂的业务逻辑,这里resultMap的优势才会被放大。因此我们通过一个案例来实现复杂环境的查询。

//创建教室表
create table classroom
(
	id int not null AUTO_INCREMENT,
	classname VARCHAR(40) not null,
	PRIMARY KEY (id)
)
//创建学生表
create table student
(
	id int not null AUTO_INCREMENT,
	name VARCHAR(40) not null,
	classid int not null,
	PRIMARY KEY (id),
	FOREIGN key (classid) REFERENCES classroom(id)
)
//创建一些数据
insert into classroom VALUES (1,'101班')
insert into classroom VALUES (2,'102班')
insert into student VALUES(1,'Amy',1);
insert into student VALUES(2,'Bob',1);
insert into student VALUES(3,'javayz',1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

在Java的实体类代码中分别建立Student和ClassRoom的类

public class Student {
    private int id;
    private String name;
    private ClassRoom classRoom;
    //省略构造方法、get、set、toString
    //务必给出无参构造方法
}
1
2
3
4
5
6
7

ClassRoom 类

public class ClassRoom {
    private int id;
    private String classname;
    //省略构造方法、get、set、toString
    //务必给出无参构造方法
}
1
2
3
4
5
6

# 2.1 多对一查询(association)

多对一查询在上面这个案例中表现为多个学生在一个教实中,因此这里的Student类中的第三个变量我们设置为ClassRoom类。

现在我们通过两种方式进行多对一查询:

在mapper路径下创建StudentMapper和studentMapper.xml

其中StudentMapper接口定义一个查询方法:

public interface StudentMapper {
    List<Student> selectAllStudent();
}
1
2
3

查询方法一:通过类似嵌套子查询的方式查询:

<select id="selectAllStudent" resultMap="StudentAndClassRoom">
    select * from student
</select>
<resultMap id="StudentAndClassRoom" type="Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--对于复杂类型,对象使用association处理,集合使用collection-->
    <association property="classRoom" column="classid" javaType="ClassRoom" select="getClassRoom">
    </association>
</resultMap>
<select id="getClassRoom" resultType="ClassRoom">
    select * from classroom where id = #{classid}
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13

首先通过 select * from student 语句查询到所有数据,返回类型通过resultMap进行映射,id和name字段属于基本数据类型字段不需要改动,由于classRoom是一个对象方法,因此需要用association 进行处理,实体类中的属性名为classRoom,与Student表进行连接的字段是classid,Java类是ClassRoom,通过这几个属性,继续使用select查询。

所以上面一段代码总结下来就是两句话:

1、查询student表。

2、通过查询出来的classid值查询classRoom表。

通过测试类测试结果:

@Test
public void testSelect(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> students = mapper.selectAllStudent();
    System.out.println(students);
}
1
2
3
4
5
6
7

结果如下:

[Student{id=1, name='Amy', classRoom=ClassRoom{id=1, classname='101班'}}, 
 Student{id=2, name='Bob', classRoom=ClassRoom{id=1, classname='101班'}},
 Student{id=3, name='javayz', classRoom=ClassRoom{id=1, classname='101班'}}]
1
2
3

查询方法二:结果嵌套查询

<select id="selectAllStudent" resultMap="StudentAndClassRoom">
    select s.id sid,s.name sname,c.id cid,c.classname cname
    from student s,classroom c
    where s.classid=c.id
</select>
<resultMap id="StudentAndClassRoom" type="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="classRoom" javaType="ClassRoom">
        <result property="id" column="cid"/>
        <result property="classname" column="cname"/>
    </association>
</resultMap>
1
2
3
4
5
6
7
8
9
10
11
12
13

我个人比较喜欢结果嵌套查询,所有的sql语句写在select语句中,根据结果要返回的值在resultMap中填写对应的数据。

结果与第一种方式相同。

# 2.2 一对多查询(collection)

修改一下之前的两个实体类,来实现一对多的查询,每个教室里有多个学生:

public class Student {
    private int id;
    private String name;
    private int classId;
    //省略get、set、toString方法
    //务必给出无参构造方法
}
1
2
3
4
5
6
7
public class ClassRoom {
    private int id;
    private String classname;
    private List<Student> students;
    //省略get、set、toString方法
    //务必给出无参构造方法
}  
1
2
3
4
5
6
7

接着编写Mapper接口和对应的Mapper.xml

public interface ClassRoomMapper {
    List<ClassRoom> getClassRoomByid(@Param("id") int id);
}
1
2
3

在这里我只介绍结果嵌套查询的方式。先通过sql查询出字段信息,再通过resultMap进行展示。

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.javayz.mapper.ClassRoomMapper">
    <select id="getClassRoomByid" resultMap="ClassRoomAndStudent" parameterType="int">
        select c.id cid,c.classname cname,s.id sid,s.name sname,s.classid classid
        from student s,classroom c
        where s.classid=c.id and c.id=#{id}
    </select>
    <resultMap id="ClassRoomAndStudent" type="ClassRoom">
        <result property="id" column="cid"/>
        <result property="classname" column="cname"/>
        <!--对于集合属性,需要使用collection来实现-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="classId" column="classid"/>
        </collection>
    </resultMap>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

需要注意几个点,对于集合属性,我们需要使用collection。这里的集合属性中用到了ofType字段,该字段表示集合泛型中所使用的泛型对象类型。

# (三)总结

到这里为止,Mybatis的各种查询以及配置用法都讲完了,对于复杂类型,注意以下几点:

1、对象的关联使用association,集合的关联使用collection。

2、官网的resultMap中还有两个参数没有使用:

4-2.png

首先讲一下constructor ,constructor主要是用来配置构造方法,默认情况下mybatis会调用实体类的无参构造方法创建一个实体类,如果在实体类中配置了有参构造方法而没有配无参构造,就会报错,此时可用constructor 注入构造方法。

discriminator 鉴别器,可以通过case的值来决定使用哪个resultMap。

上次更新: 2025/04/29, 17:22:06
Mybatis配置详细解析
Mybatis日志原来是这样使用的

← Mybatis配置详细解析 Mybatis日志原来是这样使用的→

最近更新
01
AI大模型部署指南
02-18
02
半个月了,DeepSeek为什么还是服务不可用
02-13
03
Python3.9及3.10安装文档
01-23
更多文章>
Theme by Vdoing | Copyright © 2023-2025 备案图标 浙公网安备33021202002405 | 浙ICP备2023040452号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式