MyBatis使用嵌套查询collection和association的实现

 更新时间:2024年09月27日 11:07:17   作者:永不服输的coder  
本文详细介绍了使用MyBatis框架进行数据库操作时,如何利用collection标签实现一对多的嵌套查询和使用association标签实现一对一的嵌套查询,感兴趣的可以了解一下

1、目标

本文的主要目标是MyBatis使用collection标签完成一对多的嵌套查询,使用association标签完成一对一的嵌套查询

2、一对多的嵌套查询用collection标签

2.1 多表查询并且resultMap是数据库的class表

create table `class` (
    `id` int primary key auto_increment,
    `name` varchar(100) not null,
    `create_time` date not null
);

create table `stu` (
    `id` int primary key auto_increment,
    `name` varchar(100) not null,
    `age` int not null,
    `class_id` int not null
);

先在数据库创建表class和stu,class是班级表,stu是学生表,stu学生表中包含class_id字段表示这个学生在哪个班级中

insert into `class`(`name`, `create_time`) values ('1班', now()), ('2班', now()), ('3班', now());

然后在class表中插入记录

insert into `stu`(`name`, `age`, `class_id`) values ('张三1', 20, 1), ('李四1', 18, 1), ('王五1', 21, 1),
                                                    ('张三2', 15, 2), ('李四2', 19, 2), ('王五2', 16, 2),
                                                    ('张三3', 22, 3), ('李四3', 29, 3), ('王五3', 26, 3);

然后在stu表中插入记录

@Data
public class Class {

    private String id;

    private String name;

    private String createTime;

    private List<Stu> stuList;

  @Override
  public String toString() {
    return "Class{" +
      "id='" + id + '\'' +
      ", name='" + name + '\'' +
      ", createTime='" + createTime + '\'' +
      ", stuList=" + stuList +
      '}';
  }
}

创建Class实体类,对应class表的字段,其中多了一个字段是stuList这个list集合,因为一个班级包含了多个学生,这里是一对多的嵌套查询因此用collection标签

@Data
public class Stu {

  private Integer id;

  private String name;

  private Integer age;

  private Integer classId;

  @Override
  public String toString() {
    return "Stu{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", age=" + age +
      ", classId=" + classId +
      '}';
  }

}

创建Stu实体类,对应stu表的字段

@Mapper
public interface ClassMapper {

    Class getClass(@Param("className") String className);

}

创建ClassMapper的接口方法getClass,入参是className,出参是一个Class班级对象,这个班级对象会封装多个学生对象

<?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="org.apache.mybatisDemo.ClassMapper">

    <resultMap id="classMap" type="org.apache.mybatisDemo.Class">
        <id property="id" column="classId"/>
        <result property="name" column="className"/>
        <result property="createTime" column="create_time"/>
        <collection property="stuList" javaType="java.util.List" ofType="org.apache.mybatisDemo.Stu">
            <result property="id" column="stuId"/>
            <result property="name" column="stuName"/>
            <result property="age" column="age"/>
            <result property="classId" column="class_id"/>
        </collection>
    </resultMap>

    <select id="getClass" resultMap="classMap">
        select c.id classId, c.name className, c.create_time, s.id stuId, s.name stuName, s.age, s.class_id
        from `class` c inner join `stu` s on c.id = s.class_id
        where c.`name` = #{className}
    </select>

</mapper>

sql使用多表查询,resultMap是返回值的类型,使用collection标签封装多个Stu学生对象,collection标签中javaType指定是List集合类型,ofType指定List集合的泛型类型

注意:

(1)

<id property="id" column="classId"/>
<result property="name" column="className"/>
<result property="createTime" column="create_time"/>

resultMap中除了collection的更新字段至少有1个,比如这里我更新了3个字段
如果除了collection没有更新字段,会抛出异常,具体参考我写的博客:
MyBatis嵌套查询collection报错:org.apache.ibatis.exceptions.TooManyResultsException

(2)sql查询的字段如果重复名字的要取别名,如果不取别名resultMap直接用c.id,MyBatis底层不会将这个字段作为查询字段,因为它识别不了c是哪张表

<mappers>
    <mapper resource="mybatis/ClassMapper.xml"/>
</mappers>

mybatis-config.xml文件中设置mapper标签是ClassMapper.xml,因此可以扫描到这个xml文件

@Test
  public void f1() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
      Class cls = mapper.getClass("2班");
      System.out.println(cls.toString());
    }
  }

测试这个接口方法

在这里插入图片描述

测试结果返回一个Class班级对象,这个对象包含id=2,name=2班,createTime,stuList是一个list集合,它封装了多个Stu学生对象

2.2 多表查询并且resultMap是自定义VO类ClassVO

那resultMap返回值类型必须是数据库表对应的实体类吗?不一定,返回值类型可以是自定义类比如VO

@Data
public class ClassVO {

  private String id;

  private List<Stu> stuList;

  @Override
  public String toString() {
    return "ClassVO{" +
      "id='" + id + '\'' +
      ", stuList=" + stuList +
      '}';
  }

}

自定义VO类ClassVO包含的字段只有id和stuList这个list集合

@Mapper
public interface ClassMapper {

    ClassVO getClassVO(@Param("className") String className);

}

创建接口方法,返回值类型是自定义ClassVO类

<?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="org.apache.mybatisDemo.ClassMapper">

    <resultMap id="classVOMap" type="org.apache.mybatisDemo.ClassVO">
        <id property="id" column="classId"/>
        <collection property="stuList" javaType="java.util.List" ofType="org.apache.mybatisDemo.Stu">
            <id property="id" column="stuId"/>
            <result property="name" column="stuName"/>
            <result property="age" column="age"/>
            <result property="classId" column="class_id"/>
        </collection>
    </resultMap>

    <select id="getClassVO" resultMap="classVOMap">
        select c.id classId, c.name, c.create_time, s.id stuId, s.name stuName, s.age, s.class_id
        from `class` c inner join `stu` s on c.id = s.class_id
        where c.`name` = #{className}
    </select>
</mapper>

这个接口方法的xml对应的返回值类型是type=“org.apache.mybatisDemo.ClassVO”

注意:
(1)resultMap中除了collection的更新字段至少有1个
(2)sql查询的字段如果重复名字的要取别名

@Test
  public void f2()throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
      ClassVO classVO = mapper.getClassVO("2班");
      System.out.println(classVO.toString());
    }
  }

测试getClassVO方法,返回值是一个ClassVO对象

在这里插入图片描述

测试结果是返回一个ClassVO对象,它包含id=2,stuList这个list集合,它包含了3个Stu学生对象

结论:resultMap返回值类型不一定是数据库表对应的实体类,可以是自己定义的类

2.3 单表查询并且resultMap是自定义VO类ClassVO

可以单表查询stu学生表然后resultMap可以映射吗?可以,不过除了resultMap中除了collection必须至少有1个字段更新

@Mapper
public interface ClassMapper {

    ClassVO getClasVOBySingleTable(@Param("classId") Integer classId);

}

创建一个mapper接口方法

<?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="org.apache.mybatisDemo.ClassMapper">

    <resultMap id="ClasVOBySingleTableMap" type="org.apache.mybatisDemo.ClassVO">
        <id property="id" column="class_id"/>
        <collection property="stuList" javaType="java.util.List" ofType="org.apache.mybatisDemo.Stu">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="age" column="age"/>
            <result property="classId" column="class_id"/>
        </collection>
    </resultMap>

    <select id="getClasVOBySingleTable" resultMap="ClasVOBySingleTableMap">
        select * from stu where stu.class_id = #{classId}
    </select>
</mapper>

mapper.xml文件中sql是单表查询,也可以进行resultMap的字段赋值

注意:
(1)resultMap中除了collection的更新字段至少有1个
(2)sql查询的字段如果重复名字的要取别名

@Test
  public void f3() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
      ClassVO classVO = mapper.getClasVOBySingleTable(2);
      System.out.println(classVO.toString());
    }
  }

调用getClasVOBySingleTable方法会返回ClassVO对象

在这里插入图片描述

测试结果是单表查询,也可以返回一个ClassVO对象,它包含id=2,stuList是list集合,它包含3个Stu学生对象

结论:单表查询stu学生表然后resultMap可以映射

2.4 单表查询并且resultMap是自定义VO类ClassVO,返回值是List类型

@Mapper
public interface ClassMapper {

    List<ClassVO> getClassVOList();

}

mapper接口方法的返回值是list集合

<select id="getClassVOList" resultMap="ClasVOBySingleTableMap">
   select * from stu
</select>

mapper.xml文件中单表查询,resultMap仍然是2.3中的resultMap即ClasVOBySingleTableMap,因为返回值类型list集合的泛型还是ClassVO类

@Test
  public void f4() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
      List<ClassVO> list = mapper.getClassVOList();
      list.forEach(System.out::println);
    }
  }

测试:调用getClassVOList方法

在这里插入图片描述

测试结果是单表查询返回stu学生表的所有记录,指定resultMap,封装了collection,因此会将相同class_id的多个Stu学生对象封装到一个ClassVO对象,结果是返回3个ClassVO对象,每个ClassVO对象都封装了3个Stu对象

结论:mapper接口方法的返回值是多个class_id的ClassVO对象用List集合,resultMap指定List集合的泛型,MyBatis会自动将相同class_id的Stu学生对象封装到一个ClassVO对象中

2.5 多表查询并且resultMap是数据库的class表,返回值是List类型

@Mapper
public interface ClassMapper {

    List<Class> getClassList();
    
}

返回值是List集合

<select id="getClassList" resultMap="classMap">
        select c.id classId, c.name className, c.create_time, s.id stuId, s.name stuName, s.age, s.class_id
        from `class` c inner join `stu` s on c.id = s.class_id
</select>

resultMap复用2.1的resultMap,因为返回值是list集合,它的泛型和2.1的返回值类型是相同的

@Test
  public void f5() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
      List<Class> list = mapper.getClassList();
      list.forEach(System.out::println);
    }
  }

调用getClassList方法

在这里插入图片描述

返回值是3个Class班级对象,每个Class班级对象都封装了自己班的Stu学生对象

结论:mapper接口方法的返回值是多个class_id的Class班级对象用List集合,resultMap指定List集合的泛型,MyBatis会自动将相同class_id的Stu学生对象封装到一个Class班级对象中

3、一对一的嵌套查询用association标签

3.1 多表查询,返回值是Stu学生对象

@Data
public class Stu {
  private Integer id;
  private String name;
  private Integer age;
  private Integer classId;
  private Class stuClass;
  @Override
  public String toString() {
    return "Stu{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", age=" + age +
      ", classId=" + classId +
      ", stuClass=" + stuClass +
      '}';
  }
}

创建Stu实体类对象,多一个表中不存在的Class类型的stuClass对象,它表示这个学生属于哪个班级,这里是一对一的嵌套查询因此用association标签

@Mapper
public interface StuMapper {
  Stu getStuById(@Param("stuName") String stuName);
}

mapper接口方法是getStuById方法,返回值是Stu学生对象

<?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="org.apache.mybatisDemo.StuMapper">

    <resultMap id="StuMap" type="org.apache.mybatisDemo.Stu">
        <id property="id" column="stuId"/>
        <result property="name" column="stuName"/>
        <result property="age" column="age"/>
        <result property="classId" column="class_id"/>
        <association property="stuClass" javaType="org.apache.mybatisDemo.Class">
            <id property="id" column="classId"/>
            <result property="name" column="className"/>
            <result property="createTime" column="create_time"/>
        </association>
    </resultMap>

    <select id="getStuById" resultMap="StuMap">
        select c.id classId, c.name className, c.create_time, s.id stuId, s.name stuName, s.age, s.class_id
        from `class` c inner join `stu` s on c.id = s.class_id
        where s.`name` = #{stuName}
    </select>

</mapper>

mapper.xml文件多表查询,resultMap的type是org.apache.mybatisDemo.Stu类型,其中stuClass属性是Class班级类型,是一对一的嵌套查询因此用association标签,association标签用javaType表示类型是org.apache.mybatisDemo.Class

<mappers>
    <mapper resource="mybatis/BlogMapper.xml"/>
    <mapper resource="mybatis/ClassMapper.xml"/>
    <mapper resource="mybatis/StuMapper.xml"/>
</mappers>

mybatis-config.xml文件中注册StuMapper.xml

@Test
  public void f8() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      StuMapper mapper = sqlSession.getMapper(StuMapper.class);
      Stu stu = mapper.getStuById("张三3");
      System.out.println(stu);
    }
  }

调用mapper的getStuById方法

在这里插入图片描述

测试结果是返回一个Stu学生对象,它包含id=7,name=张三3,age=22,classId=3,stuClass属性是一个Class班级对象,使用association标签封装了这个Class班级对象

结论:一对一的嵌套查询用association标签

3.2 多表查询,返回值是List集合,泛型是Stu学生对象

@Mapper
public interface StuMapper {

  List<Stu> getStuList();

}

返回值是List集合,泛型是Stu学生对象

<?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="org.apache.mybatisDemo.StuMapper">

    <select id="getStuList" resultMap="StuMap">
        select c.id classId, c.name className, c.create_time, s.id stuId, s.name stuName, s.age, s.class_id
        from `class` c inner join `stu` s on c.id = s.class_id
    </select>
    
</mapper>

mapper.xml的方法中resultMap指定StuMap,还是3.1中的resultMap,只需要指定List集合的泛型即可

@Test
  public void f9() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      StuMapper mapper = sqlSession.getMapper(StuMapper.class);
      List<Stu> stuList = mapper.getStuList();
      stuList.forEach(System.out::println);
    }
  }

调用mapper的getStuList方法

在这里插入图片描述

测试结果是查询得到9个Stu学生对象,每个学生对象都封装了stuClass这个Class班级对象

结论:一对一的嵌套查询用association标签,并且mapper接口方法返回值是List集合类型

到此这篇关于MyBatis使用嵌套查询collection和association的实现的文章就介绍到这了,更多相关MyBatis 嵌套查询collection和association内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决springboot引入swagger2不生效问题

    解决springboot引入swagger2不生效问题

    这篇文章主要为大家介绍了解决springboot引入swagger2不生效问题的方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • SpringBoot如何实现一个实时更新的进度条的示例代码

    SpringBoot如何实现一个实时更新的进度条的示例代码

    本文详细的介绍了SpringBoot如何实现一个实时更新的进度条,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Java 全排列的几种实现方法

    Java 全排列的几种实现方法

    本文详细介绍了Java中全排列问题的几种实现方法,包括回溯法、字典序排列法和迭代法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • 详解spring cloud分布式关于熔断器

    详解spring cloud分布式关于熔断器

    这篇文章主要介绍了详解spring cloud分布式关于熔断器,详细的介绍了什么是熔断器和使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • Redisson分布式锁源码解析

    Redisson分布式锁源码解析

    文章给大家分享了关于Redisson分布式锁源码相关的知识点内容,有兴趣的朋友们可以参考学习下。
    2018-08-08
  • java实现简单的验证码功能

    java实现简单的验证码功能

    这篇文章主要为大家详细介绍了java实现简单的验证码功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • 基于Java随机生成手机短信验证码的实例代码

    基于Java随机生成手机短信验证码的实例代码

    这篇文章主要介绍了Java随机生成手机短信验证码的实例代码,代码分为哦简单版和复杂版,需要的朋友可以参考下
    2019-04-04
  • Java魔法堂之调用外部程序的方法

    Java魔法堂之调用外部程序的方法

    这篇文章主要介绍了Java魔法堂:调用外部程序的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-02-02
  • 在Spring Boot中实现HTTP缓存的方法

    在Spring Boot中实现HTTP缓存的方法

    缓存是HTTP协议的一个强大功能,但由于某些原因,它主要用于静态资源,如图像,CSS样式表或JavaScript文件。本文重点给大家介绍在Spring Boot中实现HTTP缓存的方法,感兴趣的朋友跟随小编一起看看吧
    2018-10-10
  • 快速了解JAVA垃圾回收机制

    快速了解JAVA垃圾回收机制

    这篇文章主要介绍了有关Java垃圾回收机制的知识,文中实例简单易懂,方便大家更好的学习,有兴趣的朋友可以了解下
    2020-06-06

最新评论