MyBatis 高级映射与延迟加载(分步查询)的详细内容

1. MyBatis 高级映射与延迟加载(分步查询)的详细内容

文章目录

  • 1. MyBatis 高级映射与延迟加载(分步查询)的详细内容
  • 2. 准备工作
  • 3. 多对一 高级映射
    • 3.1 第一种方式:级联属性映射
    • 3.2 第二种方式:association
    • 3.3 第三种方式:分步查询
  • 4. 多对一延迟加载
  • 5. 一对多 高级映射
    • 5.1 第一种方式:collection
    • 5.2 第二种方式:分步查询
  • 6. 一对多延迟加载
  • 7. 多对多 高级映射
  • 8. 总结:
  • 9. 最后:


2. 准备工作

准备数据库表:一个班级对应多个学生。班级表:t_clazz;学生表:t_student

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


pom.xml 文件当中配置相关的依赖的 jar 包如下:

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rainbowsea</groupId>
    <artifactId>mybatis-005-crud-blog</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <!--        mybatis 的依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>

        <!--        mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!--        引入 logback的依赖,这个日志框架实现了slf4j 规范-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

</project>

配置 logback 的配置文件,用于打印显示,我们的日志信息,方便我们查看我们的运行过程,效果。

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

配置 MyBatis 的核心配置文件,

在这里插入图片描述

<?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>

        <!--  使用 <package>	还可以将这个包下的所有的类的全部自动起别名,别名就是简名,不区分大小写 -->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>
    <environments default="mybatis">

        <environment id="mybatis">
            <!--            MANAGED 没有用第三框架管理的话,都是会被提交的,没有事务上的管理了 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 这里也是可以使用 package 包名扫描,但是同样的:对应接口路径要一致,接口名一致-->
        <package name="com.rainbowsea.mybatis.mapper"></package>
    </mappers>
</configuration>

3. 多对一 高级映射

多对一的高级映射 多种方式,常见的包括三种:

  • 第一种方式:一条SQL语句,级联属性映射。
  • 第二种方式:一条SQL语句,association。
  • 第三种方式:两条SQL语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)

在这里插入图片描述

多的的一方是:Student
一的一方是: Clazz

怎么分主表和副表
原则:谁在前看,谁就是主表

多对一:多在前,那么多就是主表,
一对多:一在前,那么一就是主表

对照 t_clazz,t_stu 创建的ORM 映射的 Clazz,Student 类

注意:在MyBatis 当中对应的ORM ,一般在框架里对应的 Bean实体类,一定要实现该 set 和 get 方法以及无参数构造方法,无法框架无法使用反射机制,进行操作

建议用包装类,这样可以防止 Null的问题,因为(简单类型 int num = null ,是不可以赋值为 null)的编译无法通过

pojo类Student中添加一个属性:Clazz clazz; 表示学生关联的班级对象。

在这里插入图片描述

package com.rainbowsea.mybatis.pojo;


/**
 * 学生信息
 */
public class Student {  // Student 是多的一方
    private Integer sid;
    private String sname;
    private Clazz clazz;  // Clazz 是一的一方

    public Student() {
    }

    public Student(Integer sid, String sname, Clazz clazz) {
        this.sid = sid;
        this.sname = sname;
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", clazz=" + clazz +
                '}';
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }


}

在这里插入图片描述

package com.rainbowsea.mybatis.pojo;


/**
 * 多对一
 */
public class Clazz {

    private Integer cid;
    private String cname;


    public Clazz() {
    }

    public Clazz(Integer cid, String cname) {
        this.cid = cid;
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Clazz{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                '}';
    }

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }
}

3.1 第一种方式:级联属性映射

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Student;

import java.util.List;

public interface StudentMapper {
    /**
     * 根据id获取学生信息,同时获取学生关联的班级信息
     * @param id 学生的id
     * @return 学生对象,但是学生对象当中含有班级对象
     */
    Student selectById(Integer id);


}


resultMap 标签当中定义POJO类属性 与 数据表字段名映射关系:

在这里插入图片描述

<?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 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.StudentMapper">

    
    <resultMap id="studentResultMap" type="Student">
        <id property="sid" column="sid"></id>
        <result property="sname" column="sname"></result>
        <result property="clazz.cid" column="cid"></result>
        <result property="clazz.cname" column="cname"></result>
    </resultMap>
    
    <!--	id 要是 namespace 对应接口上的方法名: -->
    <select id="selectById" resultMap="studentResultMap">
        select s.sid,
               s.sname,
               c.cid,
               c.cname
        from t_stu s
                 left join t_clazz c on s.cid = c.cid
        where s.sid = #{sid}
    </select>

</mapper>

运行测试:

在这里插入图片描述

package com.rainbowsea.mybatis.test;

import com.rainbowsea.mybatis.mapper.StudentMapper;
import com.rainbowsea.mybatis.pojo.Student;
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.Test;

import java.io.IOException;

public class StudentMapperTest {

    @Test
    public void testSelectById() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectById(5);
        System.out.println(student);
        sqlSession.close();

    }

}

3.2 第二种方式:association

与第一种方式类似,在第一种方式的基础上只需要修改 resultMap 中的配置:association 即可。其他位置都不需要修改。

association翻译为:关联。

学生对象关联一个对象(这里关联班级对象)。

在这里插入图片描述


import com.rainbowsea.mybatis.pojo.Student;

import java.util.List;

public interface StudentMapper {
    /**
     * 一条SQL语句,association
     * @param id
     * @return
     */
    Student selectByIdAssociation(Integer id);

}
association 翻译为关联,一个student 对象关联一个Clazz对象
property,提供要映射的POJO类的属性名
javaType: 用来指定要映射的Java类(全限定类名,启用了别名可以用别名)

在这里插入图片描述

<?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 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.StudentMapper">

    <resultMap id="studentResultMapAssociation" type="Student">
        <id property="sid" column="sid"></id>
        <result property="sname" column="sname"></result>
        <!--
                association 翻译为关联,一个student 对象关联一个Clazz对象
                property,提供要映射的POJO类的属性名
                javaType: 用来指定要映射的Java类(全限定类名,启用了别名可以用别名)
                -->
        <!--        <association property="clazz" javaType="com.rainbowsea.mybatis.pojo.Clazz"></association>
        我们开启的别名机制-->
        <association property="clazz" javaType="Clazz">
            <id property="cid" column="cid"></id>
            <result property="cname" column="cname"></result>
        </association>

    </resultMap>

    <select id="selectByIdAssociation" resultMap="studentResultMapAssociation">
        select s.sid,
               s.sname,
               c.cid,
               c.cname
        from t_stu s
                 left join t_clazz c on s.cid = c.cid
        where s.sid = #{sid}
    </select>

</mapper>

运行测试:

在这里插入图片描述


import com.rainbowsea.mybatis.mapper.StudentMapper;
import com.rainbowsea.mybatis.pojo.Student;
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.Test;

import java.io.IOException;

public class StudentMapperTest {


    @Test
    public void testStudentResultMapAssociation()  throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectByIdAssociation(5);
        System.out.println(student);
        sqlSession.close();
    }



}

3.3 第三种方式:分步查询

在这里插入图片描述

分布查询的优点:

  1. 第一:复用性增强,可以重复证明。(大步拆成N多个小碎布,每一个小碎步更加可以重复利用)
  2. 第二:采用这种分步查询,可以充分利用他们的延迟加载/懒加载机制

什么是延迟加载(懒加载),有什么用?

延迟加载的核心原理是:用的时候再执行查询语句,不用的时候不查询
作用:提高性能,尽可能的不查,或者尽可能的少查,来提高效率

在 mybatis 当中怎么开启延迟加载呢?

association标签之哦给你添加 fetchType = “lazy”

注意,默认情况下是没有开启延迟加载的,需要设置,fetchType=“lazy”

这种在association标签中配置fetchType=“lazy” 是局部的设置,只对当前association关联的sql语句起作用,fetchType=“eager” 表示关闭局部的延迟加载

实际的开发中的模式:
把全局的延迟加载打开。
如果某一步不需要使用延迟加载,请设置fetchType=“eager” 即可

在这里插入图片描述


import com.rainbowsea.mybatis.pojo.Student;

import java.util.List;

public interface StudentMapper {

    /**
     *分部查询第一步,先根据学生的sid 查询学生的信息
     * @param id
     * @return
     */
    Student selectByIdStep1(Integer id);
}

其他位置不需要修改,只需要修改以及添加以下三处:

第一处:association中select位置填写sqlId。sqlId= namespace+id。其中column属性作为这条子sql语句的条件。

在这里插入图片描述

在这里插入图片描述

<?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 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.StudentMapper">



    <!--
            分布查询的优点:
                第一:复用性增强,可以重复证明。(大步拆成N多个小碎布,每一个小碎步更加可以重复利用)
                第二:采用这种分步查询,可以充分利用他们的延迟加载/懒加载机制

             什么是延迟加载(懒加载),有什么用?
             延迟加载的核心原理是:用的时候再执行查询语句,不用的时候不查询
             作用:提高性能,尽可能的不查,或者尽可能的少查,来提高效率
             在mybatis当中怎么开启延迟加载呢?
                association标签之哦给你添加fetchType="lazy"
              注意,默认情况下是没有开启延迟加载的,需要设置,fetchType="lazy"
              这种在association标签中配置fetchType="lazy" 是局部的设置,只对当前association关联的sql语句起作用
              fetchType="eager" 表示关闭局部的延迟加载
              实际的开发中的模式:
              把全局的延迟加载打开。
              如果某一步不需要使用延迟加载,请设置fetchType="eager" 即可
    -->
    <!--    两条SQL语句,完成多对一的分布查询-->
    <!--    这里是第一步,根据学生的id查询学生的所有信息,这些信息当中含有班级id(cid)-->
    <!--    type 是Java中的类集合/数组除了Map,存储的元素类型《用全限定类名,启用了别名机制,用别名
            column 数据库查询的字段名-->
    <resultMap id="studentResulMapByStep" type="Student">
        <id property="sid" column="sid"></id>
        <result property="sname" column="sname"></result>
        <!--    <association property="clazz(第一步查询的字段名(与第二步关联的字段))" select="这里需要指定另外第二步SQL语句的ID(com.rainbowsea.mybatis.mapper
        .ClazzMapper
        .selectByIdStep2)"
             column="cid 第二步SQL语句传的字段信息,查询"></association>-->
        <!--        <association property="clazz" select="com.rainbowsea.mybatis.mapper.ClazzMapper.selectByIdStep2"-->
        <!--                     column="cid" fetchType="lazy"></association>      <-->
        <association property="clazz" select="com.rainbowsea.mybatis.mapper.ClazzMapper.selectByIdStep2"
                     column="cid" fetchType="lazy"></association>
        <!--一条SQL语句一条 association-->

    </resultMap>
    <select id="selectByIdStep1" resultMap="studentResulMapByStep">
        select sid, sname, cid
        from t_stu
        where sid = #{sid}
    </select>


</mapper>

第二处:在ClazzMapper接口中添加方法

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Clazz;

public interface ClazzMapper {
    /**
     * 多对一:分布查询第二步:根据cid获取班级信息
     *
     * @param cid
     * @return
     */
    Clazz selectByIdStep2(Integer cid);


}

在这里插入图片描述

在这里插入图片描述

<?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 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.ClazzMapper">

    <!--	id 要是 namespace 对应接口上的方法名: -->

    <!--	id 要是 namespace 对应接口上的方法名: -->
    <select id="selectByIdStep2" resultType="Clazz">
        select cid, cname
        from t_clazz
        where cid = #{cid}
    </select>

</mapper>

运行测试:

在这里插入图片描述


import com.rainbowsea.mybatis.mapper.StudentMapper;
import com.rainbowsea.mybatis.pojo.Student;
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.Test;

import java.io.IOException;

public class StudentMapperTest {

    @Test
    public void testselectByIdStep1()  throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectByIdStep1(5);
        System.out.println(student);
        sqlSession.close();
    }
}

4. 多对一延迟加载

延迟加载的核心原理是:用的时候再执行查询语句,不用的时候不查询
作用:提高性能,尽可能的不查,或者尽可能的少查,来提高效率

要想支持延迟加载,非常简单,只需要在association标签中添加 fetchType=“lazy” 即可。

在这里插入图片描述

<?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 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.StudentMapper">



    <!--
            分布查询的优点:
                第一:复用性增强,可以重复证明。(大步拆成N多个小碎布,每一个小碎步更加可以重复利用)
                第二:采用这种分步查询,可以充分利用他们的延迟加载/懒加载机制

             什么是延迟加载(懒加载),有什么用?
             延迟加载的核心原理是:用的时候再执行查询语句,不用的时候不查询
             作用:提高性能,尽可能的不查,或者尽可能的少查,来提高效率
             在mybatis当中怎么开启延迟加载呢?
                association标签之哦给你添加fetchType="lazy"
              注意,默认情况下是没有开启延迟加载的,需要设置,fetchType="lazy"
              这种在association标签中配置fetchType="lazy" 是局部的设置,只对当前association关联的sql语句起作用
              fetchType="eager" 表示关闭局部的延迟加载
              实际的开发中的模式:
              把全局的延迟加载打开。
              如果某一步不需要使用延迟加载,请设置fetchType="eager" 即可
    -->
    <!--    两条SQL语句,完成多对一的分布查询-->
    <!--    这里是第一步,根据学生的id查询学生的所有信息,这些信息当中含有班级id(cid)-->
    <!--    type 是Java中的类集合/数组除了Map,存储的元素类型《用全限定类名,启用了别名机制,用别名
            column 数据库查询的字段名-->
    <resultMap id="studentResulMapByStep" type="Student">
        <id property="sid" column="sid"></id>
        <result property="sname" column="sname"></result>
        <!--    <association property="clazz(第一步查询的字段名(与第二步关联的字段))" select="这里需要指定另外第二步SQL语句的ID(com.rainbowsea.mybatis.mapper
        .ClazzMapper
        .selectByIdStep2)"
             column="cid 第二步SQL语句传的字段信息,查询"></association>-->
        <!--        <association property="clazz" select="com.rainbowsea.mybatis.mapper.ClazzMapper.selectByIdStep2"-->
        <!--                     column="cid" fetchType="lazy"></association>      <-->
        <association property="clazz" select="com.rainbowsea.mybatis.mapper.ClazzMapper.selectByIdStep2"
                     column="cid" fetchType="lazy"></association>
        <!--一条SQL语句一条 association-->

    </resultMap>
    <select id="selectByIdStep1" resultMap="studentResulMapByStep">
        select sid, sname, cid
        from t_stu
        where sid = #{sid}
    </select>

</mapper>

我们现在只查询学生名字,修改测试程序:

在这里插入图片描述


import com.rainbowsea.mybatis.mapper.StudentMapper;
import com.rainbowsea.mybatis.pojo.Student;
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.Test;

import java.io.IOException;

public class StudentMapperTest {

    @Test
    public void testSelectByIdStep1() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectByIdStep1(5);
        System.out.println("学生的姓名: " + student.getSname());
        sqlSession.close();
    }
}

如果后续需要使用到学生所在班级的名称,这个时候才会执行关联的sql语句,修改测试程序:
在这里插入图片描述


import com.rainbowsea.mybatis.mapper.StudentMapper;
import com.rainbowsea.mybatis.pojo.Student;
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.Test;

import java.io.IOException;

public class StudentMapperTest {

    @Test
    public void testSelectByIdStep1() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectByIdStep1(5);
        System.out.println("学生的姓名: " + student.getSname());

        // 到这里之后,想获取班级名字了
        String cname = student.getClazz().getCname();
        System.out.println("学生的班级名称:" + cname);
        sqlSession.close();
    }
}

通过以上的执行结果可以看到,只有当使用到班级名称之后,才会执行关联的sql语句,这就是延迟加载。

当然上述方式,仅仅只是局部设置的延迟加载(这对当前 association 关联的 SQL 语句起作用,对其他的位置时不起作用的)。association标签添加fetchType=“lazy”
注意,默认情况下是没有开启延迟加载的,需要设置,fetchType=“lazy”
这种在association标签中 配置fetchType=“lazy” 是局部的设置,只对当前association关联的 sql语句起作用
fetchType=“eager” 表示关闭局部的延迟加载

在mybatis中如何开启全局的延迟加载呢 ?需要setting配置,参考 MyBatis 中文开发手册 https://mybatis.net.cn/ 如下:

在这里插入图片描述

<settings>
  <setting name="lazyLoadingEnabled" value="true"/>
</settings>

注意:settings 标签的正确位置顺序,可以根据报错信息进行纠正。

在这里插入图片描述

<?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>

    <settings>
<!--        启用驼峰命名映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
<!--       延迟加载的全局开关,默认值是 false 不开启 (简单的说就是:所有只要但凡带有分布的,都采用延迟加载)-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
    <!--    起别名-->
    <typeAliases>
        <!--  使用 <package>	还可以将这个包下的所有的类的全部自动起别名,别名就是简名,不区分大小写 -->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>
    <environments default="mybatis">

        <environment id="mybatis">
            <!--            MANAGED 没有用第三框架管理的话,都是会被提交的,没有事务上的管理了 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 这里也是可以使用 package 包名扫描,但是同样的:对应接口路径要一致,接口名一致-->
        <package name="com.rainbowsea.mybatis.mapper"></package>
    </mappers>
</configuration>

把fetchType="lazy"去掉。 测试 全局设置是否有效。

在这里插入图片描述

执行以下程序:

在这里插入图片描述

在这里插入图片描述

通过以上的测试可以看出,我们已经开启了全局延迟加载策略。开启全局延迟加载之后,所有的sql都会支持延迟加载。如果我们想其中的某个SQL 语句你不希望它支持延迟加载怎么办呢?

可以将该SQL 语句的 fetchType 设置为 eager,就不会启用延迟加载机制的。这样的话,针对某个特定的SQL 语句,你就关闭了延迟加载机制。后期我们要不要开启延迟加载机制,主要看实际的业务需求是怎样的。

在这里插入图片描述

运行测试:

在这里插入图片描述

在这里插入图片描述

实际的开发中的模式:把全局的延迟加载打开。如果某一步不需要使用延迟加载,请设置fetchType=“eager” 即可

5. 一对多 高级映射

一对多的实现,通常是在一的一方中有List集合属性。

在 Clazz 类中添加List<Student> stus ; 属性。

在这里插入图片描述

这里的一对多:关系
public class Student{}

public class Clazz { // 一个班级对象
// 一个班级对应多个学生
// 怎么去表示这个班级对应了多个学生对象呢
// 集合或者数组都可以容纳多个元素
List<Student> studentList
一对多,一在前,一是主表,多是副表
主表: t_clazz
副表: t_stu
}

在这里插入图片描述

package com.rainbowsea.mybatis.pojo;


import java.util.List;

/**
 * 多对一
 */
public class Clazz {

    private Integer cid;
    private String cname;

    private List<Student> stus;

    public Clazz() {
    }

    public Clazz(Integer cid, String cname) {
        this.cid = cid;
        this.cname = cname;
    }

    public Clazz(Integer cid, String cname, List<Student> stus) {
        this.cid = cid;
        this.cname = cname;
        this.stus = stus;
    }


    public List<Student> getStus() {
        return stus;
    }

    public void setStus(List<Student> stus) {
        this.stus = stus;
    }

    @Override
    public String toString() {
        return "Clazz{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                ", stus=" + stus +
                '}';
    }


    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }
}

在这里插入图片描述

package com.rainbowsea.mybatis.pojo;


/**
 * 学生信息
 */
public class Student {
    private Integer sid;
    private String sname;


    public Student() {
    }


    public Student(Integer sid, String sname) {
        this.sid = sid;
        this.sname = sname;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                '}';
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }
}

5.1 第一种方式:collection

**注意:是 collection 不是 association 。 **

如果返回的映射 POJO 类当中有一个集合属性,则用 collection 标签负责处理。

-对多,这里是 collection ,collection 是集合的意思,定义集合/数组当中元素的映射信息(这里是Student 类型的映射)
 ofType 属性用来指定集合当中的元素类型,全限定类名(启用别名用别名)注意是ofType,表示“集合中的类型”。
<collection property="stus(表示POJo类中的属性)" ofType="Student"></collection>-->

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Clazz;

public interface ClazzMapper {

    /**
     * 根据班级编号查询班级信息
     * @param cid
     * @return
     */
    Clazz selectByCollection(Integer cid);
}

在这里插入图片描述

<?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 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.ClazzMapper">

    <!--	id 要是 namespace 对应接口上的方法名: -->
    <resultMap id="clazzResultMap" type="Clazz">
        <id property="cid" column="cid"></id>
        <result property="cname" column="cname"></result>
        <!--        -对多,这里是 collection ,collection 是集合的意思,定义集合/数组当中元素的映射信息(这里是Student 类型的映射)-->
        <!--        ofType 属性用来指定集合当中的元素类型,全限定类名(启用别名用别名)-->
        <!--        <collection property="stus(表示POJo类中的属性)" ofType="Student"></collection>-->
        <collection property="stus" ofType="Student">
            <id property="sid" column="sid"></id>
            <result property="sname" column="sname"></result>
        </collection>
    </resultMap>

    <select id="selectByCollection" resultMap="clazzResultMap">
        select c.cid, c.cname, s.sid, s.sname
        from t_clazz c
                 left join t_stu s on c.cid = s.cid
        where c.cid = #{cid}
    </select>

</mapper>

运行测试:

在这里插入图片描述

package com.rainbowsea.mybatis.test;

import com.rainbowsea.mybatis.mapper.ClazzMapper;
import com.rainbowsea.mybatis.pojo.Clazz;
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.Test;

import java.io.IOException;

public class ClazzMapperTest {

    @Test
    public void testSelectByCollection() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
        Clazz clazz = mapper.selectByCollection(1000);
        System.out.println(clazz);
        sqlSession.close();
    }
}


5.2 第二种方式:分步查询

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Clazz;

public interface ClazzMapper {


    /**
     * 分布查询,第一步,根据班级编号获取班级信息
     * @param cid
     * @return
     */
    Clazz selectByStep1(Integer cid);

}

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Student;

import java.util.List;

public interface StudentMapper {
    /**
     * 一对多:根据班级编号查询学生信息
     */
    List<Student> selectByCidsStep2(Integer cid);

}


在这里插入图片描述

<?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 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.ClazzMapper">


    <!--    分布查询第一步:根据班级的cid获取班级信息-->
    <resultMap id="clazzResultMapStep" type="Clazz">
        <id property="cid" column="cid"></id>
        <result property="cname" column="cname"></result>
        <collection property="stus"
                    select="com.rainbowsea.mybatis.mapper.StudentMapper.selectByCidsStep2"
                    column="cid">
        </collection>
    </resultMap>

    <select id="selectByStep1" resultMap="clazzResultMapStep">
        select cid, cname
        from t_clazz
        where cid = #{cid}
    </select>


</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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.StudentMapper">
    <!--    一对多 ,分布查询第二步-->
    <select id="selectByCidsStep2" resultType="Student">
        select sid, sname, cid
        from t_stu
        where cid = #{cid}
    </select>

</mapper>

运行测试:

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.mybatis.test;

import com.rainbowsea.mybatis.mapper.ClazzMapper;
import com.rainbowsea.mybatis.pojo.Clazz;
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.Test;

import java.io.IOException;

public class ClazzMapperTest {
    @Test
    public void testSelectByStep1() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
        Clazz clazz = mapper.selectByStep1(1000);
        //System.out.println(clazz);
        // 只访问班级名字
        System.out.println(clazz.getCname());

        // 只有用到的时候才会去执行第二步SQL
        System.out.println(clazz.getStus());
        sqlSession.close();
    }

}


6. 一对多延迟加载

一对多延迟加载机制和多对一是一样的。同样是通过两种方式:

  • 第一种:fetchType=“lazy”
  • 第二种:修改全局的配置setting,**lazyLoadingEnabled=true,**如果开启全局延迟加载,想让某个sql不使用延迟加载:fetchType=“eager”

7. 多对多 高级映射

多对多:分解成两个一对多,然后其中一个是:一对一的关系

8. 总结:

  1. 多对一的高级映射 多种方式,常见的包括三种:
    • 第一种方式:一条SQL语句,级联属性映射。
    • 第二种方式:一条SQL语句,association。
    • 第三种方式:两条SQL语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)
  2. association翻译为:关联。学生对象关联一个对象(这里关联班级对象)。
  3. 分布查询就是利用延时加载的机制。
  4. 延迟加载的核心原理是:用的时候再执行查询语句,不用的时候不查询,作用:提高性能,尽可能的不查,或者尽可能的少查,来提高效率。
  5. 在association标签中配置fetchType=“lazy” 是局部的设置,只对当前association关联的sql语句起作用,fetchType=“eager” 表示关闭局部的延迟加载。
  6. 如果返回的映射 POJO 类当中有一个集合属性,则用 collection 标签负责处理。
  7. 实际的开发中的模式:把全局的延迟加载打开。如果某一步不需要使用延迟加载,请设置fetchType=“eager” 即可
  8. 多对多:分解成两个一对多,然后其中一个是:一对一的关系

9. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/706882.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

英格索兰IngsollRang控制器过热维修讲解

【英格索兰IngsollRang控制器维修请关注】 【英格索兰IngsollRang控制器维修】 【英格索兰控制器维修】 一、IngsollRang扭矩枪控制器故障诊断 1. 检查环境温度&#xff1a;首先&#xff0c;确认工作场所的温度是否过高。如果环境温度超过设备规定的工作温度&#xff0c;可能…

刷代码随想录有感(102):动态规划——整数拆分

题干&#xff1a; 代码&#xff1a; class Solution { public:int integerBreak(int n) {vector<int>dp(n 1);dp[0] 0;dp[1] 0;dp[2] 1;for(int i 3; i < n; i){for(int j 1; j < i / 2; j){dp[i] max(dp[i], max((j) * (i - j), j * dp[i - j]));}}return…

JavaScript的数组(一维数组、二维数组、数组常用的方法调用)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

eclipse 老的s2sh(Struts2+Spring+Hibernate) 项目 用import导入直接导致死机(CPU100%)的解决

1、下载Apache Tomcat - Apache Tomcat 8 Software Downloads 图中是8.5.100的版本&#xff0c;下面的设置用的是另一个版本的&#xff0c;其实是一样。 2、先将Server配好&#xff0c;然后再进行导入操作。 2、选择jdk 当然&#xff0c;这里也可以直接“Download and instal…

无公网IP与服务器完成企业微信网页应用开发远程调试详细流程

文章目录 前言1. Windows安装Cpolar2. 创建Cpolar域名3. 创建企业微信应用4. 定义回调本地接口5. 回调和可信域名接口校验6. 设置固定Cpolar域名7. 使用固定域名校验 前言 本文主要介绍如何在企业微信开发者中心通过使用内网穿透工具提供的公网域名成功验证回调本地接口服务! …

Solr7.4.0报错org.apache.solr.common.SolrException

文章目录 org.apache.solr.common.SolrException: Exception writing document id MATERIAL-99598435990497269125316 to the index; possible analysis error: cannot change DocValues type from NUMERIC to SORTED_NUMERIC for field "opt_time"Exception writing…

为什么说掌握心理学知识成为产品经理一门必修课?

大家好&#xff0c;我是herosunly。985院校硕士毕业&#xff0c;现担任算法研究员一职&#xff0c;热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名&#xff0c;CCF比赛第二名&#xff0c;科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…

uniapp地图导航

我们只需要给图标加一个点击事件 我这里的数据都是动态的&#xff0c;想测试的朋友可以写固定值 然后跳转之后首先会调到选择软件导航 点击导航之后会显示使用哪个app 最后我们选择之后将会直接跳转到app进行导航

【启明智显彩屏应用】Model3A 7寸触摸彩屏的充电桩应用方案

一、充电桩概述 &#xff08;一&#xff09;充电桩诞生背景 随着社会的进步和人们生活质量的提升&#xff0c;汽车已逐渐融入每个家庭的日常生活中。然而&#xff0c;汽车数量的激增也带来了严重的环境污染问题&#xff0c;特别是尾气排放。为了应对这一挑战&#xff0c;新能源…

Modbus协议转Profinet协议网关与气体监测系统配置案例

一、背景&#xff1b;Modbus协议和Profinet协议作为工业领域常见的两种通讯协议&#xff0c;各自具有一定的特点和应用范围。Modbus转Profinet网关&#xff08;XD-MDPN100/300&#xff09;在工业自动化控制系统中&#xff0c;可以将Modbus协议转换为Profinet协议&#xff0c;以…

认识线性调频信号(LFM)和脉冲压缩

目录 1. 线性调频&#xff08;LFM&#xff09;信号&#xff1a;2.Matlab仿真3.脉冲压缩 微信公众号获取更多FPGA相关源码&#xff1a; 1. 线性调频&#xff08;LFM&#xff09;信号&#xff1a; 在时域中&#xff0c;一个理想的线性调频信号或脉冲持续时间为T秒&#xff0c;…

加码多肤色影像技术 这是传音找到的“出海利器“?

全球化时代&#xff0c;市场竞争愈演愈烈&#xff0c;产品差异化已然成为了企业脱颖而出的关键。在黄、白肤色长期占据人像摄影主赛道的背景下&#xff0c;传音就凭借独一无二的多肤色影像技术走出非洲&#xff0c;走向了更广阔的新兴市场。 聚焦深肤色人群拍照痛点&#xff0c…

web端使用高德地图

web端使用高德地图 一、申请高德key和秘钥二、在项目中引入所需功能js、css文件三、实现地图选点、回显选点四、自定义地图 一、申请高德key和秘钥 申请高德key 申请成功后可以得到key 二、在项目中引入所需功能js、css文件 <script src"https://webapi.amap.com/m…

【复旦邱锡鹏教授《神经网络与深度学习公开课》笔记】感知器

感知器是一种非常早期的线性分类模型&#xff0c;作为一种简单的神经网络模型被提出。感知器是一种模拟生物神经元行为的机器&#xff0c;有与生物神经元相对应的部件&#xff0c;如权重&#xff08;突触&#xff09;、偏置&#xff08;阈值&#xff09;及激活函数&#xff08;…

在项目中使用Volta控制node版本

在项目中使用Volta控制node版本 前端的技术很多年前就已经是井喷的状态了&#xff0c;基本每一年都有技术的迭代&#xff0c;而node作为前端基石之一也是一年一个样&#xff0c;这就导致了不同年代的版本使用不同的项目会经常出问题&#xff0c;高版本的node无法用于老的项目。…

【设计模式】行为型-模板方法模式

方法千变万化&#xff0c;心灵如潮&#xff0c;模板如画&#xff0c;画出生活的韵味。 文章目录 一、茶与咖啡二、模板方法模式三、模板方法模式的核心组成四、运用模板方法模式五、模板方法模式的应用场景六、小结推荐阅读 一、茶与咖啡 场景假设&#xff1a;我们需要完成茶…

测试开发面经分享,面试七天速成 DAY2

1. TCP和UDP的区别 a. TCP是面向连接的协议&#xff0c;而UDP是无连接的协议。 b. TCP提供可靠的数据传输&#xff0c;保证数据的有序性和完整性&#xff0c;而UDP则不提供这些保证。 c. TCP使用流控制和拥塞控制机制&#xff0c;以确保数据的可靠传输&#xff0c;而UDP没有这…

HCIE-QOS基本原理

QOS基本原理 QOS概述什么是QOSQoS服务模型区分服务模型QoS常用技术 (DiffServ模型)QoS数据处理流程 (DiffServ模型) QoS流分类和流标记QoS数据处理流程为什么需要流分类和流标记 简单流分类外部优先级 - VLAN报文外部优先级 - MPLS报文外部优先级 - IP报文各外部优先级间的对应…

Kafka集成flume

1.flume作为生产者集成Kafka kafka作为flume的sink&#xff0c;扮演消费者角色 1.1 flume配置文件 vim $kafka/jobs/flume-kafka.conf # agent a1.sources r1 a1.sinks k1 a1.channels c1 c2# Describe/configure the source a1.sources.r1.type TAILDIR #记录最后监控文件…

计算机视觉基础课程知识点总结

图像滤波 相关: 核与图像同向应用&#xff0c;不翻转。 卷积: 核在应用前翻转&#xff0c;广泛用于信号处理和深度学习&#xff08;现在常说的二维卷积就是相关&#xff09;。 内积: 向量化的点积操作&#xff0c;是相关和卷积的一部分。 模板匹配&#xff1a;通过在图像中…