MyBatis

MyBatis 是一款优秀的半自动的ORM持久层框架,它支持自定义 SQL、存储过程以及高级映射。

MyBatis 支持几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

持久层:可以立即保存在磁盘上,在这里可以理解为与数据库相关操作。

  1. 什么是ORM

    OBject Relation Mapping 对象关系映射

    对象指的是面向面向对象,关系指的是数据库中的表,

    例如Java语言中的POJO类与数据库模型之间的对应关系。

    orm

  2. 为什么MyBatis是半自动ORM框架

    public class User(){
        private long id;
        private String realname;
    }
    
    User user = user .getById(3);
    
    SELECT `id`,`realname` FROM `user` WHERE `id` = 3;
    

    以上用例为ORM框架部分执行代码,发现在ORM框架中不需要使用SQL语句,

    大大减少了程序员学习成本和SQL语句维护成本,

    另外当数据库产品更换的之后无需重新编写项目中的SQL语句

    -- MySQL
    SELECT * FROM `user` LIMIT 10;
    
    -- SqlServer
    SELECT * FROM `user` TOP 10;
    

    用MyBatis进行开发的,需要手写SQL语句,而ORM框架例如Hibernate则不需要编写SQL语句。

  3. 为什么MyBais是半自动ORM框架我们还要去学习呢

    因为MyBatis使用自定义SQL所以更加灵活尤其在多表操作

MyBaits工作原理

JDBC核心对象

  1. DriverManager,数据库驱动管理对象
  2. Connection,数据库连接对象
  3. Statement | PrepareStatement ,操作数据库SQL语句对象
  4. ResultSet,结果集对象

MyBaits核心对象

  1. SqlSession对象,该对象包含了执行SQL语句的所有方法,例如JDBC里面Connection

    对象说明
    Executor接口执行器.统一调度其他三个对象执行对应的SQL
    StatementHandler使用的数据库中的Statement执行操作 相当于字符串拼接
    PrepareStatement使用SQL传参的方式处理
    ResultHandler对最后的结果进行封装
  2. Executor接口,将传递过来的参数动态生成SQL语句,负责查询缓存。

  3. MappedStatement对象,该对象负责对SQL封装,用于存储需要映射的SQL语句及参数等信息

  4. ResultHandler对象,用户返回结果集合,封装成最红想要的数据类型,可以自定义返回类型

MyBatis执行流程

MyBatis执行流程

快速上门

新建maven项目并导入相关依赖

mvn archetype:generate
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.lhz</groupId>
  <artifactId>mybatis</artifactId>
  <version>1.0.0</version>
  <name>mybatis</name>
  <packaging>jar</packaging>

  <properties>
    <jdk.version>21</jdk.version>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <maven.compiler.compilerVersion>21</maven.compiler.compilerVersion>
    <maven.compiler.encoding>utf-8</maven.compiler.encoding>
    <project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <maven.test.failure.ignore>true</maven.test.failure.ignore>
    <maven.test.skip>true</maven.test.skip>
    <junit.version>5.10.2</junit.version>
    <commons-dbutils.version>1.8.1</commons-dbutils.version>
    <commons-io.version>2.16.1</commons-io.version>
    <commons-lang3.version>3.14.0</commons-lang3.version>
    <dom4j.version>2.1.4</dom4j.version>
    <druid.version>1.2.22</druid.version>
    <fastjson.version>2.0.49</fastjson.version>
    <hibernate.version>6.5.1.Final</hibernate.version>
    <hutool.version>5.8.27</hutool.version>
    <generator.version>1.1.2</generator.version>
    <guava.version>33.0.0-jre</guava.version>
    <gson.version>2.10.1</gson.version>
    <jackson.version>2.17.0</jackson.version>
    <jaxen.version>2.0.0</jaxen.version>
    <lombok.version>1.18.32</lombok.version>
    <mybatis.version>3.5.16</mybatis.version>
    <mysql.version>8.4.0</mysql.version>
    <spring.version>6.1.6</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>RELEASE</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.github.binarywang</groupId>
      <artifactId>java-testdata-generator</artifactId>
      <version>${generator.version}</version>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>${hutool.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>${commons-io.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>${commons-lang3.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>${gson.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson.version}</version>
    </dependency>
    <!--jackson-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
    <dependency>
      <groupId>org.dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>${dom4j.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/jax33.0.0-jreen/jaxen -->
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>${jaxen.version}</version>
    </dependency>
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>${guava.version}</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${druid.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-dbutils</groupId>
      <artifactId>commons-dbutils</artifactId>
      <version>${commons-dbutils.version}</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>

  </dependencies>


  <build>
    <!--项目打包文件名-->
    <finalName>${project.name}</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.4.0</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.4.1</version>
      </plugin>
      <!-- 编译级别 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
        <configuration>
          <!-- 设置编译字符编码 -->
          <encoding>UTF-8</encoding>
          <!-- 设置编译jdk版本 -->
          <source>${jdk.version}</source>
          <target>${jdk.version}</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.3.2</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.3.1</version>
      </plugin>
      <!-- 打包的时候跳过测试junit begin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <!--<version>2.22.2</version>-->
        <version>3.2.5</version>
        <configuration>
          <skip>true</skip>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

根据数据表生成对应POJO类

orm

编写数据源文件

src/main/resources/config.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?allowPublicKeyRetrieval=true&useUnicode=true&createDatabaseIfNotExist=true&characterEncoding=UTF8&useSSL=false&useServerPrepStmts=false&rewriteBatchedStatements=true&cachePrepStmts=true&allowMultiQueries=true&serverTimeZone=Asia/Shanghai&sslMode=DISABLED
username=root
password=lihaozhe

编写核心配置文件

src/main/resources/mybatis/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>
    <!--
        这些属性可以在外部进行配置,并可以进行动态替换。
        你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
    -->
    <properties resource="config.properties" />
    <!--
        MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
        例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
    -->
    <!--默认使用的环境 ID(比如:default="development")。-->
    <environments default="development">
        <!--每个 environment 元素定义的环境 ID(比如:id="development")。-->
        <environment id="development">
            <!--事务管理器的配置(比如:type="JDBC")。-->
            <!--
                事务管理器(transactionManager)
                在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
                JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
                MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
                默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
                如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
                这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
            -->
            <transactionManager type="JDBC"/>
            <!--数据源的配置(比如:type="POOLED")-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    
</configuration>

Mybatis核心配置文件

测试数据库连通性

package cn.lhz.mybatis;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class MyBatisTest {
  private static final Log log = LogFactory.getLog(MyBatisTest.class);

  @Test
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    // 从SqlSessionFactory对象中获取SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    log.info(sqlSession.getConnection());
    // 归还连接给数据源
    sqlSession.close();
  }
}

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 查询所有用户 resultType
   *
   * @return 用户列表
   */
  List<Person> selectAll4t();
}

编写映射配置文件

src/main/java/cn/lhz/mapper/PersonMapper.xml

或者

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
    <!--
      select  用于查询
      id      与接口中方法的名字同名 即调用接口中方法的时候 该方法会找到该配置文件中对应的SQL
      resultType 返回值数据类型 或 泛型为该数据类型的集合
    -->

    <!--查询用户列表 resultType-->
    <select id="selectAll4t" resultType="cn.lhz.pojo.Person">
        SELECT * FROM `person`
    </select>
</mapper>

mybatis映射配置文件

核心配置文件加载映射配置文件

<!--
   既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。
   但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
   在自动查找资源方面,Java 并没有提供一个很好地解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
   你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。
  -->
  <mappers>
    <!-- 使用相对于类路径的资源引用 -->
    <!--单独加载某个映射配置文件-->
    <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
    <!-- 使用完全限定资源定位符(URL) -->
    <!--<mapper url="file:///home/lhz/Documents/code/mybatis/src/main/resources/mybatis/mapper/PersonMapper.xml"/>-->
    <!--<mapper url="file:///D:/mybatis/src/main/resources/cn/lhz/mapper/PersonMapper.xml"/>-->
    <!--加载某包下所有的映射配置文件-->
    <package name="cn.lhz.mapper"/>
  </mappers>

完整核心配置文件

<?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>
  <!--
      这些属性可以在外部进行配置,并可以进行动态替换。
      你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
  -->
  <properties resource="config.properties"/>
  <!--
      MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
      例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
  -->
  <!--默认使用的环境 ID(比如:default="development")。-->
  <environments default="development">
    <!--每个 environment 元素定义的环境 ID(比如:id="development")。-->
    <environment id="development">
      <!--事务管理器的配置(比如:type="JDBC")。-->
      <!--
          事务管理器(transactionManager)
          在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
          JDBC – 这个配置直接使用了 JDBC 的提交和回滚设置,它依赖从数据源获得的连接来管理事务作用域。
          MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
          默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
          如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
          这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
      -->
      <transactionManager type="JDBC"/>
      <!--数据源的配置(比如:type="POOLED")-->
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!--
   既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。
   但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
   在自动查找资源方面,Java 并没有提供一个很好地解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
   你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。
  -->
  <mappers>
    <!-- 使用相对于类路径的资源引用 -->
    <!--单独加载某个映射配置文件-->
    <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
    <!-- 使用完全限定资源定位符(URL) -->
    <!--<mapper url="file:///home/lhz/Documents/code/mybatis/src/main/resources/mybatis/mapper/PersonMapper.xml"/>-->
    <!--<mapper url="file:///D:/mybatis/src/main/resources/cn/lhz/mapper/PersonMapper.xml"/>-->
    <!--加载某包下所有的映射配置文件-->
    <package name="cn.lhz.mapper"/>
  </mappers>
</configuration>

测试

src/test/java/cn/lhz/mapper/PersonMapperTest.java

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private SqlSessionFactory sqlSessionFactory;

  @BeforeEach
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

  @Test
  public void selectAll4t() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取该接口的代理对象
    PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
    //  执行接口中的方法
    List<Person> persons = personMapper.selectAll4t();
    // 释放SqlSession资源
    sqlSession.close();
    persons.forEach(System.out::println);
  }
}

测试结果发现 idCard属性获取到的结果为null,

这是由于查询字段中没有idCard字段,

换言之 idCard属性 没有与 id_card字段形成映射关系

解决方法有两种:

  1. 使用ResultMap做结果集映射
  2. 在核心配置文件中设置 mapUnderscoreToCamelCase 值为 true

解决驼峰命名与下划线映射关系

使用 resultMap

映射配置文件中编写 resultMap

<!--描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。-->
<!--
	resultMap
	id 在命名空间中唯一的标识符
	type 结果集映射的javabean 或者 泛型为该类型的集合
-->
<resultMap id="PersonMap" type="cn.lhz.pojo.Person">
    <!--
		id 主键映射
		result 非主键映射
		property javabean 中的 filed
		column datatable 中的 filed
    -->
    <id property="id" column="id"/>
    <result property="id" column="id"/>
    <result property="uuid" column="uuid"/>
    <result property="mobile" column="mobile"/>
    <result property="nickname" column="nickname"/>
    <result property="idCard" column="id_card"/>
</resultMap>

修改select id=“selectAll4m” 的属性 resultType=“cn.lhz.pojo.Person” 为 resultMap=“PersonMap”

<select id="selectAll4m" resultMap="PersonMap">
    SELECT *
    FROM `person`
</select>

在接口中增加对应的方法

package cn.lhz.mapper;

import cn.lhz.pojo.Person;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 查询所有用户 resultType
   *
   * @return 用户列表
   */
  List<Person> selectAll4t();

  /**
   * 查询所有用户 resultTap
   *
   * @return 用户列表
   */
  List<Person> selectAll4m();
}

测试 selectAll4m 方法

@Test
public void selectAll4m(){
    // 获取该接口的代理对象
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
    // 执行接口中的方法
    List<Person> people = mapper.selectAll4m();
    // 释放SqlSession资源
    sqlSession.close();
    // 遍历集合
    people.forEach(System.out::println);
}
配置 mapUnderscoreToCamelCase 值为 true

在核心配置文件中配置此参数如下

<settings>
    <!-- 驼峰命名与下划线自动转换 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

测试 selectAll4t 方法

@Test
public void selectAll4t() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取该接口的代理对象
    PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
    //  执行接口中的方法
    List<Person> persons = personMapper.selectAll4t();
    // 释放SqlSession资源
    sqlSession.close();
    persons.forEach(System.out::println);
}

在控制台显示log日志

在核心配置文件中 设置logImpl 的值为 STDOUT_LOGGING

<settings>
    <!-- log日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

完整的核心配置文件

src/main/resources/mybatis/myatis-confog.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>
  <!--
      这些属性可以在外部进行配置,并可以进行动态替换。
      你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
  -->
  <properties resource="config.properties"/>
  <!--
      MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
      例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
  -->
  <settings>
    <!-- 驼峰命名与下划线自动转换 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- log日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
  </settings>
  <!--默认使用的环境 ID(比如:default="development")。-->
  <environments default="development">
    <!--每个 environment 元素定义的环境 ID(比如:id="development")。-->
    <environment id="development">
      <!--事务管理器的配置(比如:type="JDBC")。-->
      <!--
          事务管理器(transactionManager)
          在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
          JDBC – 这个配置直接使用了 JDBC 的提交和回滚设置,它依赖从数据源获得的连接来管理事务作用域。
          MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
          默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
          如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
          这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
      -->
      <transactionManager type="JDBC"/>
      <!--数据源的配置(比如:type="POOLED")-->
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!--
   既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。
   但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
   在自动查找资源方面,Java 并没有提供一个很好地解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
   你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。
  -->
  <mappers>
    <!-- 使用相对于类路径的资源引用 -->
    <!--单独加载某个映射配置文件-->
    <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
    <!-- 使用完全限定资源定位符(URL) -->
    <!--<mapper url="file:///home/lhz/Documents/code/mybatis/src/main/resources/mybatis/mapper/PersonMapper.xml"/>-->
    <!--<mapper url="file:///D:/mybatis/src/main/resources/cn/lhz/mapper/PersonMapper.xml"/>-->
    <!--加载某包下所有的映射配置文件-->
    <package name="cn.lhz.mapper"/>
  </mappers>
</configuration>

select

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 根据手机号查询用户信息
   *
   * @param mobile 手机号
   * @return Person对象
   */
  Person selectByMobile(@Param("mobile") String mobile);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.xml

默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。

<?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="cn.lhz.mapper.PersonMapper">

    <!--根据手机号查询用户信息-->
    <select id="selectByMobile" parameterType="java.lang.String" resultType="cn.lhz.pojo.Person">
        SELECT *
        FROM `person`
		WHERE `mobile` = #{mobile}>
    </select>

</mapper>

mybatis传参配置

测试类

cn.lhz.mapper.PersonMapperTest

注意:测试方法中获取SqlSession对象

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
  private SqlSessionFactory sqlSessionFactory;

  @BeforeEach
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

  @Test
  public void selectByMobile() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取该接口的代理对象
    PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
    //  执行接口中的方法
    Person person = personMapper.selectByMobile("13420797910");
    // 释放SqlSession资源
    sqlSession.close();
    log.info(person.toString());
  }
}

#与$的区别

默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。

这样做更安全,更迅速,通常也是首选做法,不过有时你就是想直接在 SQL 语句中直接插入一个不转义的字符串。

比如 ORDER BY 子句,MyBatis 就不会修改或转义该字符串了。

需求:查询的条件字段和该字段的值不确定

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 根据不确定的字段查询
   *
   * @param column 查询字段
   * @param value  查询值
   * @return Person对象
   */
  Person selectByFiled(@Param("column") String column, @Param("value") String value);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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="cn.lhz.mapper.PersonMapper">

	<!--根据不确定的字段查询-->
    <select id="selectByFiled" resultType="cn.lhz.pojo.Person">
        SELECT *
        FROM `person`
        WHERE ${column} = #{value}
    </select>
</mapper>

测试类

cn.lhz.mapper.PersonMapperTest

注意:测试方法中获取SqlSession对象

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
  private SqlSessionFactory sqlSessionFactory;

  @BeforeEach
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }


  @Test
  public void selectByFiled() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取该接口的代理对象
    PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
    //  执行接口中的方法
    Person person = personMapper.selectByFiled("mobile", "13420797910");
    // 释放SqlSession资源
    sqlSession.close();
    log.info(person.toString());
  }
}

解析结果:

==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE mobile = ?
==> Parameters: 13420797910(String)

mybatis使用$和#区别

别名映射

起因:parameterType 和 resultType 的值 需要使用类的完全限定名 太麻烦 希望使用 简短的名字来替代 类的完全限定名

在核心配置文件中 配置如下内容:

<typeAliases>
    <!--
		别名
		type	类的完全限定名
		alias	别名
    -->
    <!--为某个类指定别名-->
    <typeAlias alias="person" type="cn.lhz.pojo.Person"/>
</typeAliases>

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 根据唯一身份标识符用户信息
   *
   * @param uuid uuid
   * @return Person对象
   */
  Person selectByUuid(@Param("uuid") String uuid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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="cn.lhz.mapper.PersonMapper">
    <!--根据唯一身份标识符用户信息-->
    <select id="selectByUuid" resultType="person">
        SELECT *
        FROM `person`
        WHERE uuid = #{uuid}
    </select>
</mapper>

测试类

cn.lhz.mapper.PersonMapperTest

注意:测试方法中获取SqlSession对象

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
  private SqlSessionFactory sqlSessionFactory;

  @BeforeEach
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

  @Test
  public void selectByUuid() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取该接口的代理对象
    PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
    //  执行接口中的方法
    Person person = personMapper.selectByUuid("10dacc1548804eafbdc64e28bb608ee5");
    // 释放SqlSession资源
    sqlSession.close();
    log.info(person.toString());
  }
}

某个包下所有类做别名映射

起因:在核心配置文件中单独为某个类做别名映射 如果需要映射的类太多 则过于麻烦 可以指定某个包面所有的类指定别名映射

<typeAliases>
    <!--
		别名
		type	类的完全限定名
        alias	别名
	-->
    <!--为某个类指定别名-->
    <!--<typeAlias alias="person" type="cn.lhz.pojo.Person"/>-->
    <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
    <package name="cn.lhz.pojo"/>
</typeAliases>

完整核心配置文件

src/main/resources/mybatis/myatis-confog.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>
  <!--
      这些属性可以在外部进行配置,并可以进行动态替换。
      你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
  -->
  <properties resource="config.properties"/>
  <!--
      MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
      例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
  -->
  <settings>
    <!-- 驼峰命名与下划线自动转换 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- log日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
  </settings>
  <typeAliases>
    <!--
		别名
		type	类的完全限定名
		alias	别名
    -->
    <!--为某个类指定别名-->
    <!--<typeAlias alias="person" type="cn.lhz.pojo.Person"/>-->
    <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
    <package name="cn.lhz.pojo"/>
  </typeAliases>
  <!--默认使用的环境 ID(比如:default="development")。-->
  <environments default="development">
    <!--每个 environment 元素定义的环境 ID(比如:id="development")。-->
    <environment id="development">
      <!--事务管理器的配置(比如:type="JDBC")。-->
      <!--
          事务管理器(transactionManager)
          在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
          JDBC – 这个配置直接使用了 JDBC 的提交和回滚设置,它依赖从数据源获得的连接来管理事务作用域。
          MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
          默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
          如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
          这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
      -->
      <transactionManager type="JDBC"/>
      <!--数据源的配置(比如:type="POOLED")-->
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!--
   既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。
   但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
   在自动查找资源方面,Java 并没有提供一个很好地解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
   你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。
  -->
  <mappers>
    <!-- 使用相对于类路径的资源引用 -->
    <!--单独加载某个映射配置文件-->
    <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
    <!-- 使用完全限定资源定位符(URL) -->
    <!--<mapper url="file:///home/lhz/Documents/code/mybatis/src/main/resources/mybatis/mapper/PersonMapper.xml"/>-->
    <!--<mapper url="file:///D:/mybatis/src/main/resources/cn/lhz/mapper/PersonMapper.xml"/>-->
    <!--加载某包下所有的映射配置文件-->
    <package name="cn.lhz.mapper"/>
  </mappers>
</configuration>

insert

编写接口

cn.lhz.mapper.DeptMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface DeptMapper {
  /**
   * 新增部门
   *
   * @param dept 部分
   * @return 数据库中受影响行数
   */
  int insert(@Param("dept") Dept dept);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/DeptMapper.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="cn.lhz.mapper.DeptMapper">
  <!--新增部门-->
  <insert id="insert">
    insert into dept (`dept_name`)
    values (#{dept.deptName})
  </insert>
</mapper>

测试类

cn.lhz.mapper.DeptMapperTest

注意:测试方法中获取SqlSession对象

package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class DeptMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
  private SqlSessionFactory sqlSessionFactory;

  @BeforeEach
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

  @Test
  void insert() {
    SqlSession sqlSession = sqlSessionFactory.openSession(false);
    // 获取代理对象
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    // 准备数据
    Dept dept = new Dept("摸鱼部");
    log.info("插入之前 dept >>> " + dept.toString());
    mapper.insert(dept);
    // 手动提交事务
    sqlSession.commit();
    // 释放资源
    sqlSession.close();
    log.info("插入之后 dept >>> " + dept.toString());
  }
}

解析结果:

cn.lhz.mapper.PersonMapperTest insert
信息: 插入之前 dept >>> Dept(deptId=0, deptName=财务部)
Opening JDBC Connection
Created connection 333693383.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@13e3c1c7]
==>  Preparing: insert into dept (`dept_name`) values (?)
==> Parameters: 财务部(String)
<==    Updates: 1
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@13e3c1c7]
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@13e3c1c7]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@13e3c1c7]
Returned connection 333693383 to pool.
cn.lhz.mapper.PersonMapperTest insert
信息: 插入之后 dept >>> Dept(deptId=0, deptName=财务部)

新增数据后获取新增数据主键值

<?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="cn.lhz.mapper.DeptMapper">
  <!--新增部门-->
  <!--
    useGeneratedKeys参数默认是false,
    在进行insert操作后是获取不到自增的id的,
    如果我们需要用到主键需要将参数值改为true
    keyProperty javabean 中主键 filed
    keyColumn datatable 中主键 filed
  -->
  <insert id="insert" useGeneratedKeys="true" keyProperty="deptId" keyColumn="dept_id">
    insert into dept (`dept_name`)
    values (#{dept.deptName})
  </insert>
</mapper>

解析结果:

cn.lhz.mapper.PersonMapperTest insert
信息: 插入之前 dept >>> Dept(deptId=0, deptName=摸鱼部)
Opening JDBC Connection
Created connection 1157943921.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4504d271]
==>  Preparing: insert into dept (`dept_name`) values (?)
==> Parameters: 摸鱼部(String)
<==    Updates: 1
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4504d271]
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4504d271]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4504d271]
Returned connection 1157943921 to pool.
cn.lhz.mapper.PersonMapperTest insert
信息: 插入之后 dept >>> Dept(deptId=12, deptName=摸鱼部)

update

编写接口

cn.lhz.mapper.DeptMappe

package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface DeptMapper {
  /**
   * 修改部门信息
   *
   * @param dept 部门
   * @return 数据库中受影响行数
   */
  int update(@Param("dept") Dept dept);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/DeptMapper.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="cn.lhz.mapper.DeptMapper">
  <!--修改部门信息-->
  <update id="update">
    update dept
    set dept_name = #{dept.deptName}
    where dept_id = #{dept.deptId}
  </update>
</mapper>

测试类

cn.lhz.mapper.DeptMappeTest

package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class DeptMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
  private SqlSessionFactory sqlSessionFactory;

  @BeforeEach
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

  @Test
  void update() {
    SqlSession sqlSession = sqlSessionFactory.openSession(false);
    // 获取代理对象
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    // 准备数据
    Dept dept = new Dept(5, "企宣部");
    mapper.update(dept);
    // 手动提交事务
    sqlSession.commit();
    // 释放资源
    sqlSession.close();
  }
}

delete

编写接口

cn.lhz.mapper.DeptMappe

package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface DeptMapper {
  /**
   * 根据部门id删除部门
   *
   * @param deptId 部门ID
   * @return 数据库中受影响行数
   */
  int deleteById(@Param("deptId") long deptId);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/DeptMapper.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="cn.lhz.mapper.DeptMapper">
  <!--根据部门id删除部门-->
  <delete id="deleteById">
    delete
    from dept
    where dept_id = #{deptId}
  </delete>
</mapper>

测试类

cn.lhz.mapper.DeptMappeTest

package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class DeptMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
  private SqlSessionFactory sqlSessionFactory;

  @BeforeEach
  public void testConnection() throws IOException {
    // 核心配置文件 classpath 路径
    String resource = "mybatis/mybatis-config.xml";
    // 加载配置文件
    Reader reader = Resources.getResourceAsReader(resource);
    // 构建会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

  @Test
  void delete() {
    SqlSession sqlSession = sqlSessionFactory.openSession(false);
    // 获取代理对象
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    mapper.deleteById(5);
    // 手动提交事务
    sqlSession.commit();
    // 释放资源
    sqlSession.close();
  }
}

工具类

cn.lhz.util.mybatis.MyBatisUtil

package cn.lhz.util.mybatis;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.Reader;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class MyBatisUtil {
  private static final ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
  private static final SqlSessionFactory sqlSessionFactory;

  static {
    try {
      // 核心配置文件 classpath 路径
      String resource = "mybatis/mybatis-config.xml";
      // 加载配置文件
      Reader reader = Resources.getResourceAsReader(resource);
      // 构建会话工厂
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * 获取 SqlSession 对象
   *
   * @param autoCommit 布尔值 是否启用 自动提交
   * @return SqlSession 对象
   */
  public static SqlSession openSqlSession(boolean autoCommit) {
    if (threadLocal.get() == null) {
      // 从SqlSessionFactory对象中获取SqlSession
      SqlSession sqlSession = sqlSessionFactory.openSession(autoCommit);
      // 与线程绑定
      threadLocal.set(sqlSession);
      // 返回 SqlSession 对象
      return sqlSession;
    }
    return threadLocal.get();
  }

  /**
   * 获取 SqlSession 对象
   *
   * @param executorType 枚举值 SIMPLE REUSE BATCH
   * @param autoCommit   布尔值 是否启用 自动提交
   * @return SqlSession 对象
   */
  public static SqlSession openSqlSession(ExecutorType executorType, boolean autoCommit) {
    if (threadLocal.get() == null) {
      // 从SqlSessionFactory对象中获取SqlSession
      SqlSession sqlSession = sqlSessionFactory.openSession(executorType, autoCommit);
      // 与线程绑定
      threadLocal.set(sqlSession);
      // 返回 SqlSession 对象
      return sqlSession;
    }
    return threadLocal.get();
  }

  /**
   * 释放资源
   */
  public static void close() {
    SqlSession session = threadLocal.get();
    if (session != null) {
      threadLocal.remove();
      session.close();
    }
  }

  /**
   * 事务提交 释放资源
   */
  public static void commitAndClose() {
    SqlSession session = threadLocal.get();
    if (session != null) {
      threadLocal.remove();
      session.commit();
      session.close();
    }
  }

  /**
   * 事务提交 释放资源
   */
  public static void rollbackAndClose() {
    SqlSession session = threadLocal.get();
    if (session != null) {
      threadLocal.remove();
      session.rollback();
      session.close();
    }
  }
}

动态SQL

mybatis动态SQL

package cn.lhz.mapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class EmpMapperTest {
  private static final Log log = LogFactory.getLog(EmpMapperTest.class);

  @Test
  public void test() {
    // 需求:查询某部门的(dept_id)  名字有某个字的(emp_name) 员工列表
    // 分析:
    // 如果不给部门编号则在所有部门中查找 dept_id  不能为 0
    // 如果有部门编号则在某部门内查找
    long deptId = 10;
    String empName = "李";
    StringBuilder sql = new StringBuilder("select * from emp where 1=1");
    if (deptId != 0) {
      sql.append(" and dept_id=").append(deptId);
    }
    if (empName != null) {
      sql.append(" and emp_name like ").append("%").append(empName).append("%");
    }
    log.info(sql.toString());
    // select * from person where 1=1
    // select * from person where 1=1 and deptId = ?
    // select * from person where 1=1 and emp_name like  %?%
    // select * from person where 1=1 and deptId = ? and emp_name like  %?%
  }
}

起因:根据不同的查询条件生成不同的SQL语句

sql片段

将经常使用的SQL语句定义成SQL片段当使用的时候引导该片段即可 有点像 变量的赋值与引用

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 根据用户ID查询该用户信息 使用SQL片段
   *
   * @return 用户
   */
  Person selectById(@Param("id") long id);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--定义SQL片段-->
  <sql id="select_person">
   SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` 
  </sql>

  <!--根据用户ID查询该用户信息 使用SQL片段-->
  <select id="selectById" resultType="person">
    <include refid="select_person"/>
    where id = #{id};
  </select>
</mapper>
编写测试类

cn.lhz.mapper.PersonMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
 
  @Test
  public void selectById() {
    PersonMapper personMapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person = personMapper.selectById(3);
    MyBatisUtil.close();
    log.info(person.toString());
  }
}

SQL解析结果:

==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
==> Parameters: 3(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 3, 5bc69d3c6aba4a61a81115ba916237f3, 13925135946, 邹庶, 313227198110042264
<==      Total: 1

mybatis SQL 片段

bind

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 模糊查找某姓名中包含某个字的用户 bind
   *
   * @param nickname 关键词
   * @return Person列表
   */
  List<Person> selectByNickname(@Param("nickname") String nickname);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--定义SQL片段-->
  <sql id="select_person">
    SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person`
  </sql>

  <!--模糊查找某姓名中包含某个字的用户 bind-->
  <select id="selectByNickname" resultType="person">
    <bind name="like_nickname" value="'%' + nickname + '%'"/>
    <include refid="select_person"/>
    where `nickname` like #{like_nickname}
  </select>  
</mapper>

注意:在bind标签中value属性获取值的时候不需要使用#

编写测试类

cn.lhz.mapper.PersonMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  @Test
  public void selectByNickname() {
    PersonMapper personMapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    List<Person> personList = personMapper.selectByNickname("李");
    MyBatisUtil.close();
    personList.forEach(System.out::println);
  }
}

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:51
 */
public class PersonMapperTest {
    private SqlSession sqlSession;

    // JUnit5 @BeforeAll和@BeforeEach注解替换了 JUnit4 中的@Before注解。
    // 它⽤于表⽰应在当前类中的每个@Test⽅法之前执⾏注解⽅法。
    // 注意:@BeforeAll注解的⽅法必须为静态⽅法,否则它将引发运⾏时错误。
    // 注意:@BeforeEach注解的⽅法不得为静态⽅法,否则它将引发运⾏时错误。
    @BeforeEach
    public void getSqlSession() throws IOException {
        // 核心配置文件classpath路径
        String resource = "mybatis/mybatis-config.xml";
        // 加载配置文件
        Reader reader = Resources.getResourceAsReader(resource);
        // 构建会话工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        // 从SqlSessionFactory对象中获取SqlSession
        sqlSession = sqlSessionFactory.openSession();
    }

    @Test
    public void selectByNickname() {
       // 获取接口代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 执行代理对象中的方法
        List<Person> personList = mapper.selectByNickname("孙");
        // 释放资源
        sqlSession.close();
        // 输出结果
        personList.forEach(System.out::println);
    }
}

结果解析:

==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `nickname` LIKE ?
==> Parameters: %孙%(String)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 2, 82bbfb207b38447e93fffe3c53ba3e5b, 15051340785, 孙陈, 919948197901282822
<==        Row: 31, 3db353f3b81b44c6bc6095487d8a5b05, 13628389232, 孙鹃祈, 314211197412052754
<==        Row: 72, 2032a25e5245454894e192d8d5b17147, 13352587230, 孙茎, 453355198107241539
<==      Total: 3

if

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 模糊查找某姓名中包含某个字的男性或女性用户 if
   *
   * @param gender   性别 1代表男性 0代表女性
   * @param nickname 关键词
   * @return Person列表
   */
  List<Person> selectByGenderAndNickname(@Param("gender") Integer gender, @Param("nickname") String nickname);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--定义SQL片段-->
  <sql id="select_person">
    SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person`
  </sql>

  <!--模糊查找某姓名中包含某个字的男性或女性用户 if-->
  <select id="selectByGenderAndNickname" resultType="person">
    <include refid="select_person"/>
    where
      <if test="gender != null and (gender == 1 or gender == 0)">
       if(mod(substr(id_card,17,1),2),1,0) = #{gender}
      </if>
      <if test="nickname != null and nickname.length &gt; 0">
        <bind name="like_nickname" value="'%' + nickname + '%'"/>
        and `nickname` like #{like_nickname}
      </if>
    </where>
  </select>

</mapper>

结果解析:

-- 当方法中参数nickname的值为null或者字符串长度为零
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` where if(mod(substr(id_card,17,1),2),1,0) = ?
-- 当方法中参数nickname的值不为null且字符串长度大于零
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` where if(mod(substr(id_card,17,1),2),1,0) = ? and `nickname` like ?
编写测试类

cn.lhz.mapper.PersonMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  @Test
  public void selectByGenderAndNickname() {
    PersonMapper personMapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    // List<Person> personList = personMapper.selectByGenderAndNickname(0,null);
    // List<Person> personList = personMapper.selectByGenderAndNickname(1,"孙");
    List<Person> personList = personMapper.selectByGenderAndNickname(null, "孙");
    MyBatisUtil.close();
    personList.forEach(System.out::println);
  }
}

where

使用上面if相同的方法selectByGenderAndNickname和配置文件

当两个参数同时为null的时候sql语句为 :

SELECT * FROM `perason` WHERE 

这条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">
<!--
	mapper		映射配置
	namespace	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--定义SQL片段-->
  <sql id="select_person">
    SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person`
  </sql>

  <!--模糊查找某姓名中包含某个字的男性或女性用户 if-->
  <select id="selectByGenderAndNickname" resultType="person">
    <include refid="select_person"/>
    <where>
      <if test="gender != null and (gender == 1 or gender == 0)">
       and if(mod(substr(id_card,17,1),2),1,0) = #{gender}
      </if>
      <if test="nickname != null and nickname.length &gt; 0">
        <bind name="like_nickname" value="'%' + nickname + '%'"/>
        and `nickname` like #{like_nickname}
      </if>
    </where>
  </select>

</mapper>

choose (when, otherwise)

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

需求:id, uuid, mobile 多个查询条件选择其中一个

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * `id`, `uuid`, `mobile` 多个查询条件选择其中一个
   *
   * @param person 查询套件
   * @return Person对象
   */
  Person selectByIdOrUuidOrMobile(@Param("person") Person person);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--定义SQL片段-->
  <sql id="select_person">
    SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card`
    FROM `person`
  </sql>

  <!--`id`, `uuid`, `mobile` 多个查询条件选择其中一个-->
  <select id="selectByIdOrUuidOrMobile" resultType="person">
    <include refid="select_person"/>
    <where>
      <choose>
        <when test="person.id != null and person.id &gt; 0">
          id = #{person.id}
        </when>
        <when test="person.uuid != null and person.uuid.length == 32">
          uuid = #{person.uuid}
        </when>
        <when test="person.mobile != null and person.mobile.length == 11">
          mobile = #{person.mobile}
        </when>
        <otherwise>
          1 != 1
        </otherwise>
      </choose>
    </where>
  </select>
</mapper>

解析结果:

-- 传入id
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?

-- 传入uuid
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `uuid` = ?

-- 只传入mobile
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `mobile` = ?

-- 不传任何参数
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE 1 != 1
编写测试类

cn.lhz.mapper.PersonMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  @Test
  public void selectByIdOrUuidOrMobile() {
    // 获取该接口的代理对象
    PersonMapper personMapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person = new Person();
    // person.setId(1);
    // person.setUuid("5ee0074d47064285b8c5925534d70d84");
    // person.setMobile("13651227685");
    //  执行接口中的方法
    person = personMapper.selectByIdOrUuidOrMobile(person);
    // 释放SqlSession资源
    MyBatisUtil.close();
    log.info(person.toString());
  }
}

trim

编写接口

cn.lhz.mapper.EmpMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Emp;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface EmpMapper {
  /**
   * 动态SQL if 按照部门ID和姓名中部分关键字查找
   * 如果没有传递部门ID则在所有部门中查找
   * 如果传递部门ID但没有传递姓名中的关键字则查找该部门下所有员工列表
   * 如果部门ID和姓名中的关键字都没有传递则查找所有员工列表
   *
   * @param emp 查询条件
   * @return Emp列表
   */
  List<Emp> selectByDeptIdOrEmpName(@Param("emp") Emp emp);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/EmpMapper.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="cn.lhz.mapper.EmpMapper">
  <!--
   动态SQL if 按照部门ID和姓名中部分关键字查找
   如果没有传递部门ID则在所有部门中查找
   如果传递部门ID但没有传递姓名中的关键字则查找该部门下所有员工列表
   如果部门ID和姓名中的关键字都没有传递则查找所有员工列表
   -->

  <select id="selectByDeptIdOrEmpName" resultType="emp">
    select * from emp
    <trim prefix="where" prefixOverrides="AND|OR">
      <if test="emp.deptId != null and emp.deptId &gt; 0">
        and dept_id = #{emp.deptId}
      </if>
      <if test="emp.empName != null and emp.empName.length &gt; 0">
        <bind name="like_emp_name" value="'%' + emp.empName + '%'"/>
        and emp_name like #{like_emp_name}
      </if>
    </trim>
  </select>
</mapper>

解析 结果:

-- emp_name ""  
select * from emp

-- dept_id 1
select * from emp where dept_id = ?

-- emp_name 孙
select * from emp where emp_name like ?

-- dept_id 1 并且 emp_name 孙
select * from emp where dept_id = ? and emp_name like ?
编写测试类

cn.lhz.mapper.EmpMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Emp;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class EmpMapperTest {
  private static final Log log = LogFactory.getLog(EmpMapperTest.class);

  @Test
  public void selectByDeptIdOrEmpName() {
    EmpMapper mapper = MyBatisUtil.getMapper(EmpMapper.class, true);
    Emp emp = new Emp();
    emp.setDeptId(1);
    emp.setEmpName("孙");
    List<Emp> empList = mapper.selectByDeptIdOrEmpName(emp);
    empList.forEach(System.out::println);
  }
}

set

用于动态更新语句的类似解决方案叫做 set, set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。

需求:更新 person表 中的个别字段

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 修改用户表数据
   *
   * @param person 用户被修改的数据
   * @return 数据库中受影响行数
   */
  int updateById(@Param("person") Person person);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--修改用户表数据-->
  <update id="updateById">
    update person
    <set>
      <if test="person.nickname != null and person.nickname.length &gt; 0">
        nickname = #{person.nickname},
      </if>
      <if test="person.mobile != null and person.mobile.length ==  11">
        mobile = #{person.mobile},
      </if>
      <if test="person.idCard != null and (person.idCard.length == 18 or person.idCard.length == 15)">
        id_card = #{person.idCard}
      </if>
    </set>
    <where>
      id = #{person.id}
    </where>
  </update>
</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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--修改用户表数据-->
  <update id="updateById">
    update person
    <trim prefix="set" suffixOverrides=",">
      <if test="person.nickname != null and person.nickname.length &gt; 0">
        nickname = #{person.nickname},
      </if>
      <if test="person.mobile != null and person.mobile.length ==  11">
        mobile = #{person.mobile},
      </if>
      <if test="person.idCard != null and (person.idCard.length == 18 or person.idCard.length == 15)">
        id_card = #{person.idCard},
      </if>
    </trim>
    <where>
      id = #{person.id}
    </where>
  </update>
</mapper>
编写测试类

cn.lhz.mapper.PersonMapperTest

package cn.lhz.mapper;

import cn.binarywang.tools.generator.ChineseMobileNumberGenerator;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  @Test
  public void updateById() {
    PersonMapper mapper = MyBatisUtil.getMapper(PersonMapper.class, false);
    // 准备数据
    Person person = new Person();
    String mobile = ChineseMobileNumberGenerator.getInstance().generate();
    log.info("mobile:" + mobile);
    person.setMobile(mobile);
    person.setId(3);
    int row = mapper.updateById(person);
    MyBatisUtil.commitAndClose();
    log.info("row:" + row);
  }
}

foreach

select
编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 查找多个id的用户列表 foreach
   *
   * @param ids 用户id数组
   * @return Person列表
   */
  List<Person> selectByIds(@Param("ids") Long... ids);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
    <!--定义SQL片段-->
  <sql id="select_person">
    SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card`
    FROM `person`
  </sql>

  <!--查找多个id的用户列表 foreach-->
  <select id="selectByIds" resultType="person">
    <include refid="select_person"/>
    <where>
      <choose>
        <when test="ids != null and ids.length &gt; 0">
          id in
          <foreach collection="ids" index="i" item="id" open="(" separator="," close=") ">
            #{id}
          </foreach>
        </when>
        <otherwise>
          1 != 1
        </otherwise>
      </choose>
    </where>
  </select>
</mapper>
编写测试类

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.binarywang.tools.generator.ChineseMobileNumberGenerator;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  @Test
  public void selectByIds() {
    PersonMapper personMapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    // List<Person> personList = personMapper.selectByIds();
    // List<Person> personList = personMapper.selectByIds(1L);
    List<Person> personList = personMapper.selectByIds(1L, 3L, 6L);
    MyBatisUtil.close();
    personList.forEach(System.out::println);
  }
}

delete
编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {

  /**
   * 根据传入的id删除用户 foreach
   *
   * @param ids 用户id数组
   * @return 数据库中受影响行数
   */
  int delByIds(@Param("ids") Long... ids);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--根据传入的id删除用户 foreach-->
  <delete id="delByIds">
    delete from person
    <where>
      <choose>
        <when test="ids != null and ids.length &gt; 0">
          id in
          <foreach collection="ids" index="i" item="id" open="(" separator="," close=") ">
            #{id}
          </foreach>
        </when>
        <otherwise>
          1 != 1
        </otherwise>
      </choose>
    </where>
  </delete>
</mapper>
编写测试类

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.binarywang.tools.generator.ChineseMobileNumberGenerator;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  @Test
  public void delByIds() {
    PersonMapper personMapper = MyBatisUtil.getMapper(PersonMapper.class, false);
    // int row = personMapper.delByIds();
    // int row = personMapper.delByIds(6L);
    int row = personMapper.delByIds(1L, 3L);
    MyBatisUtil.commitAndClose();
    log.info("row:" + row);
  }
}

insert

清空person表

truncate person;
编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 新增用户列表 foreach
   *
   * @param personList 用户列表
   * @return 数据库中受影响行数
   */
  int insertAll(@Param("personList") List<Person> personList);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--新增用户列表 foreach-->
  <insert id="insertAll">
    <choose>
      <when test="personList != null and personList.size() &gt; 0">
        INSERT INTO `person` (`uuid`,`mobile`,`nickname`,`id_card`) VALUES
        <foreach collection="personList" index="i" item="person" separator=",">
          (#{person.uuid},#{person.mobile},#{person.nickname},#{person.idCard})
        </foreach>
      </when>
      <otherwise>
        select 0 from dual
      </otherwise>
    </choose>
  </insert>
</mapper>

sql解析 当为正确执行 insert 语句 返回结果为 -1

Opening JDBC Connection
Created connection 1100109058.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41925502]
==>  Preparing: select 0 from dual
==> Parameters: 
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41925502]
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41925502]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@41925502]
Returned connection 1100109058 to pool.
cn.lhz.mapper.PersonMapperTest insertAll
信息: row:-1
编写测试类

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.binarywang.tools.generator.ChineseIDCardNumberGenerator;
import cn.binarywang.tools.generator.ChineseMobileNumberGenerator;
import cn.binarywang.tools.generator.ChineseNameGenerator;
import cn.hutool.core.util.IdUtil;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  @Test
  public void insertAll() {
    PersonMapper personMapper = MyBatisUtil.getMapper(PersonMapper.class, false);
    // int row = personMapper.insertAll(null);
    // int row = personMapper.insertAll(new ArrayList<>());
    List<Person> personList = new ArrayList<>();
    for (int i = 0; i < 50; i++) {
      Person person = new Person();
      person.setUuid(IdUtil.fastSimpleUUID());
      person.setMobile(ChineseMobileNumberGenerator.getInstance().generate());
      person.setNickname(ChineseNameGenerator.getInstance().generate());
      person.setIdCard(ChineseIDCardNumberGenerator.getInstance().generate());
      personList.add(person);
    }
    int row = personMapper.insertAll(personList);
    MyBatisUtil.commitAndClose();
    log.info("row:" + row);
  }
}

select

一对一

一对一

更新 person 表中 mobile字段值跟新为 login 表中的 account字段值

方法一

子查询

不安全的查询:不带where的Update语句一次更新所有表行

update person set mobile = (select mobile from login where login.person_id = person.id);
方法二

使用内连接查询

不安全的查询:不带where的Update语句一次更新所有表行

update login inner join person on login.person_id = person.id set person.mobile = login.account;
方法三

使用逗号操作符

update login,person set person.mobile = login.account where login.person_id = person.id;
resultType
编写接口

cn.lhz.mapper.LoginMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Login;
import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface LoginMapper {
  /**
   * 使用账号和密码登录 获取用户信息 resultType实现一对一查询
   *
   * @param login 登录参数 account password
   * @return 用户信息
   */
  Person selectPersonByAccountAndPassword01(@Param("login") Login login);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/LoginMapper.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="cn.lhz.mapper.LoginMapper">
  <!--resultType实现一对一查询-->
  <select id="selectPersonByAccountAndPassword01" parameterType="login" resultType="person">
    select person.id, person.nickname from login inner join person
    on login.person_id = person.id and account = #{login.account} and auth_text = #{login.authText}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.LoginMapper.Test

package cn.lhz.mapper;

import cn.lhz.pojo.Login;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class LoginMapperTest {

  private static final Log log = LogFactory.getLog(LoginMapperTest.class);

  @Test
  void selectPersonByAccountAndPassword01() {
    LoginMapper mapper = MyBatisUtil.getMapper(LoginMapper.class, true);
    Person person = mapper.selectPersonByAccountAndPassword01(new Login("15878277580", "123456"));
    if (person != null) {
      log.info(person.toString());
    }
  }
}
resultMap 同一映射配置文件内
编写接口

cn.lhz.mapper.LoginMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Login;
import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface LoginMapper {
  /**
   * 使用账号和密码登录 获取用户信息 resultMap实现一对一查询
   *
   * @param login 登录参数 account password
   * @return 用户信息
   */
  Person selectPersonByAccountAndPassword02(@Param("login") Login login);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/LoginMapper.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="cn.lhz.mapper.LoginMapper">
   <!--resultMap实现一对一查询-->
  <resultMap id="PersonMap" type="person">
    <id property="id" column="id"/>
    <result property="uuid" column="uuid"/>
    <result property="mobile" column="mobile"/>
    <result property="nickname" column="nickname"/>
    <result property="idCard" column="id_card"/>
  </resultMap>
  <select id="selectPersonByAccountAndPassword02" parameterType="login" resultMap="PersonMap">
    select person.id, person.nickname from login inner join person
    on login.person_id = person.id and account = #{login.account} and auth_text = #{login.authText}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.LoginMapper.Test

package cn.lhz.mapper;

import cn.lhz.pojo.Login;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class LoginMapperTest {

  private static final Log log = LogFactory.getLog(LoginMapperTest.class);

  @Test
  void selectPersonByAccountAndPassword02() {
    LoginMapper mapper = MyBatisUtil.getMapper(LoginMapper.class, true);
    Person person = mapper.selectPersonByAccountAndPassword02(new Login("15878277580", "123456"));
    if (person != null) {
      log.info(person.toString());
    }
  }
}
resultMap 跨射配置文件内
编写接口

cn.lhz.mapper.LoginMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Login;
import cn.lhz.pojo.Person;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface LoginMapper {
  /**
   * 使用账号和密码登录 获取用户信息 resultMap跨文件实现一对一查询
   *
   * @param login 登录参数 account password
   * @return 用户信息
   */
  Person selectPersonByAccountAndPassword03(@Param("login") Login login);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/LoginMapper.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="cn.lhz.mapper.LoginMapper">
  <!--resultMap跨文件实现一对一查询-->
  <select id="selectPersonByAccountAndPassword03" parameterType="login"
          resultMap="cn.lhz.mapper.PersonMapper.PersonMap">
   select person.id, person.nickname from login inner join person
    on login.person_id = person.id and account = #{login.account} and auth_text = #{login.authText}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.LoginMapper.Test

package cn.lhz.mapper;

import cn.lhz.pojo.Login;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class LoginMapperTest {

  private static final Log log = LogFactory.getLog(LoginMapperTest.class);

  @Test
  void selectPersonByAccountAndPassword03() {
    LoginMapper mapper = MyBatisUtil.getMapper(LoginMapper.class, true);
    Person person = mapper.selectPersonByAccountAndPassword03(new Login("15878277580", "123456"));
    if (person != null) {
      log.info(person.toString());
    }
  }
}
association
编写VO类

cn.lhz.vo.PersonVo

package cn.lhz.vo;

import cn.lhz.pojo.Login;
import cn.lhz.pojo.Person;
import lombok.*;

import java.io.Serial;
import java.io.Serializable;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class PersonVo implements Serializable {
  @Serial
  private static final long serialVersionUID = -2348274078216811958L;
  /**
   * 登录信息
   */
  private Login login;
  /**
   * 用户信息
   */
  private Person person;

  public PersonVo(Login login) {
    this.login = login;
  }

  public PersonVo(Person person) {
    this.person = person;
  }
}

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.vo.PersonVo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 根据id获取用户详细信息包括登录信息 association
   *
   * @param id 用户ID
   * @return PersonVo对象
   */
  PersonVo selectPersonVoById(@Param("id") long id);
}

在核心配置文件配置别名映射
<typeAliases>
  <package name="cn.lhz.pojo"/>
  <package name="cn.lhz.vo"/>
</typeAliases>
编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--根据id获取用户详细信息包括登录信息 association-->
  <resultMap id="PersonVoMap" type="personVo">
    <association property="login">
      <id property="id" column="login_id"/>
      <result property="account" column="account"/>
      <result property="authText" column="auth_text"/>
      <result property="personId" column="person_id"/>
    </association>
    <association property="person">
      <id property="id" column="person_id"/>
      <result property="uuid" column="uuid"/>
      <result property="mobile" column="mobile"/>
      <result property="nickname" column="nickname"/>
      <result property="idCard" column="id_card"/>
    </association>
  </resultMap>
  <select id="selectPersonVoById" resultMap="PersonVoMap">
    select login.id  as login_id,
           account,
           auth_text,
           person_id,
           person.id as person_id,
           uuid,
           mobile,
           nickname,
           id_card
    from login inner join person on login.person_id = person.id and person.id = #{id}
  </select>
</mapper>

编写测试类

cn.lhz.mapper.PersonMapperTest

package cn.lhz.mapper;

import cn.binarywang.tools.generator.ChineseIDCardNumberGenerator;
import cn.binarywang.tools.generator.ChineseMobileNumberGenerator;
import cn.binarywang.tools.generator.ChineseNameGenerator;
import cn.hutool.core.util.IdUtil;
import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.PersonVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  @Test
  void selectPersonVoById() {
    PersonVo personVo = MyBatisUtil.getMapper(PersonMapper.class, true).selectPersonVoById(1);
    MyBatisUtil.close();
    log.info(personVo.toString());
  }
}

多对一

写法与一对一完全一致

下面的案例使用关联查询和子查询分别实现

编写VO类

cn.lhz.vo.SonVo

package cn.lhz.vo;

import cn.lhz.pojo.Father;
import lombok.*;

import java.io.Serial;
import java.io.Serializable;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class SonVo implements Serializable {
  @Serial
  private static final long serialVersionUID = -7211378790562307239L;
  /**
   * 儿子ID
   */
  private long sId;
  /**
   * 儿子姓名
   */
  private String sName;
  /**
   * 该儿子父亲ID
   */
  private long fId;
  /**
   * 该儿子父亲信息
   */
  private Father father;

}

联合查询方式

编写接口

cn.lhz.mapper.SonMapper

package cn.lhz.mapper;

import cn.lhz.vo.SonVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface SonMapper {
  /**
   * 根据儿子ID查询儿子和该儿子父亲的信息 association 多对一
   *
   * @param sid 儿子ID
   * @return SonVo对象
   */
  SonVo selectSonVoBySid01(@Param("sid") long sid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/SonMapper.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="cn.lhz.mapper.SonMapper">
  <!--根据儿子ID查询儿子和该儿子父亲的信息 association 多对一-->
  <resultMap id="SonVoMap" type="sonVo">
    <id property="sId" column="s_id"/>
    <result property="sName" column="s_name"/>
    <result property="fId" column="fid"/>
    <association property="father">
      <id property="fId" column="fid"/>
      <result property="fName" column="f_name"/>
    </association>
  </resultMap>
  <select id="selectSonVoBySid01" resultMap="SonVoMap">
    select s_id, s_name, s.f_id as fid, f_name
    from son s inner join father f on s.f_id = f.f_id and s_id = #{sid}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.SonMapperTest

package cn.lhz.mapper;

import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.SonVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 下午3:13
 */
public class SonMapperTest {
    @Test
    public void selectSonVoBySid() {
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        SonMapper mapper = sqlSession.getMapper(SonMapper.class);
        SonVo sonVo = mapper.selectSonVoBySid(3L);
        MyBatisUtil.close();
        System.out.println(sonVo);
    }
}

子查询方式

编写接口

cn.lhz.mapper.FatherMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Father;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface FatherMapper {
  /**
   * 根据父亲ID获取父亲信息
   *
   * @param fid 父亲id
   * @return Father对象
   */
  Father selectFatherById(@Param("fid") long fid);
}

cn.lhz.mapper.SonMapper

package cn.lhz.mapper;

import cn.lhz.vo.SonVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface SonMapper {
  /**
   * 根据儿子ID查询儿子和该儿子父亲的信息 association 子查询 多对一
   *
   * @param sid 儿子ID
   * @return SonVo对象
   */
  SonVo selectSonVoBySid02(@Param("sid") long sid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/FatherMapper.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="cn.lhz.mapper.FatherMapper">
  <!--根据父亲ID获取父亲信息-->
  <select id="selectFatherById" resultType="father">
    select * from father where f_id = #{fid}
  </select>
</mapper>

src/main/resources/cn/lhz/mapper/SonMapper.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="cn.lhz.mapper.SonMapper">
  <!--根据儿子ID查询儿子和该儿子父亲的信息 association 子查询多对一-->
  <resultMap id="sonVoMap" type="sonVo">
    <id property="sId" column="s_id"/>
    <result property="sName" column="s_name"/>
    <result property="fId" column="f_id"/>
    <association property="father" column="f_id" select="cn.lhz.mapper.FatherMapper.selectFatherById" />
  </resultMap>
  <select id="selectSonVoBySid02" resultMap="sonVoMap">
    select * from son where s_id = #{sid}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.FatherMapperTest

package cn.lhz.mapper;

import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.SonVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class SonMapperTest {
  private static final Log log = LogFactory.getLog(SonMapperTest.class);

  @Test
  public void selectSonVoBySid02() {
    SonMapper mapper = MyBatisUtil.getMapper(SonMapper.class, true);
    SonVo sonVo = mapper.selectSonVoBySid02(1);
    MyBatisUtil.close();
    if (sonVo != null) {
      log.info(sonVo);
    }
  }
}

cn.lhz.mapper.SonMapperTest

package cn.lhz.mapper;

import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.SonVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class SonMapperTest {
  private static final Log log = LogFactory.getLog(SonMapperTest.class);

  @Test
  public void selectSonVoBySid02() {
    SonMapper mapper = MyBatisUtil.getMapper(SonMapper.class, true);
    SonVo sonVo = mapper.selectSonVoBySid02(1);
    MyBatisUtil.close();
    if (sonVo != null) {
      log.info(sonVo);
    }
  }
}

解析SQL

==>  Preparing: SELECT s_id, s_name,f_id FROM son WHERE s_id = ?
==> Parameters: 1(Long)
<==    Columns: s_id, s_name, f_id
<==        Row: 1, 李金吒, 1
====>  Preparing: SELECT f_id, f_name FROM father WHERE f_id = ?
====> Parameters: 1(Integer)
<====    Columns: f_id, f_name
<====        Row: 1, 李靖
<====      Total: 1
<==      Total: 1

一对多

下面的案例使用子查询和联合查询分别实现

编写VO类

cn.lhz.vo.FatherVo

package cn.lhz.vo;

import cn.lhz.pojo.Son;
import lombok.*;

import java.io.Serial;
import java.io.Serializable;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class FatherVo implements Serializable {
  @Serial
  private static final long serialVersionUID = -3959029238859286551L;
  /**
   * 父亲ID
   */
  private long fId;
  /**
   * 父亲姓名
   */
  private String fName;
  /**
   * 该父亲的孩子们
   */
  private List<Son> sonList;
}

子查询方式

编写接口

cn.lhz.mapper.SonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Son;
import cn.lhz.vo.SonVo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface SonMapper {
  /**
   * 根据父亲ID获取该父亲的孩子列表
   *
   * @param fid 父亲ID
   * @return 该父亲的孩子列表
   */
  List<Son> selectAllByFId(@Param("fid") long fid);
}

cn.lhz.mapper.FatherMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Father;
import cn.lhz.vo.FatherVo;
import org.apache.ibatis.annotations.Param;
  /**
   * 根据父亲ID获取父亲及其孩子们的信息 子查询
   *
   * @param fid 父亲ID
   * @return FatherVo对象
   */
  FatherVo selectFatherVoById(@Param("fid") long fid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/SonMapper.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="cn.lhz.mapper.SonMapper">
  <!--根据父亲ID获取该父亲的孩子列表-->
  <select id="selectAllByFId" resultType="son">
    select * from son where f_id = #{fid};
  </select>
</mapper>

src/main/resources/cn/lhz/mapper/FatherMapper.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="cn.lhz.mapper.FatherMapper">
  <!--根据父亲ID获取父亲及其孩子们的信息 子查询-->
  <resultMap id="FatherVoMap" type="fatherVo">
    <id property="fId" column="f_id"/>
    <result property="fName" column="f_name"/>
    <collection property="sonList" ofType="son" column="f_id" select="cn.lhz.mapper.SonMapper.selectAllByFId"/>
  </resultMap>
  <select id="selectFatherVoById" resultMap="FatherVoMap">
    select * from father where f_id = #{fid}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.SonMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Son;
import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.SonVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class SonMapperTest {
  private static final Log log = LogFactory.getLog(SonMapperTest.class);
  @Test
  public void selectAllByFId() {
    List<Son> sons = MyBatisUtil.getMapper(SonMapper.class, true).selectAllByFId(1);
    MyBatisUtil.close();
    if (!sons.isEmpty()) {
      sons.forEach(System.out::println);
    }
  }
}

cn.lhz.mapper.FatherMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Father;
import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.FatherVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class FatherMapperTest {

  private static final Log log = LogFactory.getLog(FatherMapperTest.class);

  @Test
  public void selectFatherVoById() {
    FatherMapper mapper = MyBatisUtil.getMapper(FatherMapper.class, true);
    FatherVo fatherVo = mapper.selectFatherVoById(1);
    MyBatisUtil.close();
    log.info(fatherVo.toString());
  }
}

解析SQL

==>  Preparing: SELECT f_id, f_name FROM father WHERE f_id = ?
==> Parameters: 1(Long)
<==    Columns: f_id, f_name
<==        Row: 1, 李靖
====>  Preparing: SELECT s_id, s_name,f_id FROM son WHERE f_id = ?
====> Parameters: 1(Integer)
<====    Columns: s_id, s_name, f_id
<====        Row: 1, 李金吒, 1
<====        Row: 2, 李木吒, 1
<====        Row: 3, 李哪吒, 1
<====      Total: 3
<==      Total: 1

联合查询方式

编写接口

cn.lhz.mapper.FatherMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Father;
import cn.lhz.vo.FatherVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface FatherMapper {
  /**
   * 根据父亲ID获取父亲及其儿子们的信息 联合查询
   *
   * @param fid 父亲ID
   * @return FatherVo对象
   */
  FatherVo selectFatherVoByFid(@Param("fid") long fid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/FatherMapper.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="cn.lhz.mapper.FatherMapper">
  <!--根据父亲ID获取父亲及其儿子们的信息 联合查询-->
  <resultMap id="fatherVoMap" type="fatherVo">
    <id property="fId" column="fid"/>
    <result property="fName" column="f_name"/>
    <collection property="sonList" ofType="son">
      <id property="sId" column="s_id"/>
      <result property="sName" column="s_name"/>
      <result property="fId" column="fid"/>
    </collection>
  </resultMap>
  <select id="selectFatherVoByFid" resultMap="fatherVoMap">
    select f.f_id as fid, f_name,s_id,s_name from father f inner join son s
    on f.f_id = s.f_id and f.f_id = #{fid}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.FatherMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Father;
import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.FatherVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class FatherMapperTest {

  private static final Log log = LogFactory.getLog(FatherMapperTest.class);

  @Test
  public void selectFatherVoByFid() {
    FatherVo fatherVo = MyBatisUtil.getMapper(FatherMapper.class, true).selectFatherVoByFid(1);
    MyBatisUtil.close();
    log.info(fatherVo.toString());
  }
}

多对多

  • 直接多对多
  • 间接多对多 将多对多拆成两个一对多 或 两个多对一
直接多对多

多对多

需求:查找某图书类目下的所有图书信息

编写VO类

cn.lhz.vo.CategoryVo

package cn.lhz.vo;

import cn.lhz.pojo.Book;
import lombok.*;

import java.io.Serial;
import java.io.Serializable;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class CategoryVo implements Serializable {
  @Serial
  private static final long serialVersionUID = -6630916882597086889L;
  /**
   * 分类ID
   */
  private long categoryId;
  /**
   * 分类名称
   */
  private String categoryName;
  /**
   * 某分类的图书列表
   */
  private List<Book> books;
}

编写接口

cn.lhz.mapper.CategoryMapper

package cn.lhz.mapper;

import cn.lhz.vo.CategoryVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface CategoryMapper {
  /**
   * 根据分类ID获取分类及其分类下的图书列表 直接多对多
   *
   * @param cid 图书类别ID
   * @return 图书类别详细信息
   */
  CategoryVo selectCategoryVoByCid(@Param("cid") long cid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/CategoryMapper.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="cn.lhz.mapper.CategoryMapper">
  <!--根据分类ID获取分类及其分类下的图书列表 直接多对多-->
  <resultMap id="CategoryVoMap" type="categoryVo">
    <id property="categoryId" column="category_id"/>
    <result property="categoryName" column="category_name"/>
    <collection property="books" ofType="book">
      <id property="bookId" column="book_id"/>
      <result property="bookName" column="book_name"/>
    </collection>
  </resultMap>
  <select id="selectCategoryVoByCid" resultMap="CategoryVoMap">
    select c.category_id,c.category_name ,b.book_id,book_name
     from category c inner join book_category bc inner join book b
     on c.category_id = bc.category_id and bc.book_id = b.book_id
     and c.category_id = #{cid};
  </select>
</mapper>
编写测试类

cn.lhz.mapper.CategoryMapperTest

package cn.lhz.mapper;

import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.CategoryVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class CategoryMapperTest {

  private static final Log log = LogFactory.getLog(CategoryMapperTest.class);

  @Test
  void selectCategoryVoByCid() {
    CategoryMapper mapper = MyBatisUtil.getMapper(CategoryMapper.class, true);
    CategoryVo categoryVo = mapper.selectCategoryVoByCid(1L);
    MyBatisUtil.close();
    if (categoryVo != null) {
      log.info(categoryVo.toString());
    }
  }
}

需求:查找某图书的分类信息

编写VO类

cn.lhz.vo.BookVo

package cn.lhz.vo;

import cn.lhz.pojo.Category;
import lombok.*;

import java.io.Serial;
import java.io.Serializable;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class BookVo implements Serializable {
  @Serial
  private static final long serialVersionUID = 4105586443189639418L;
  /**
   * 书的ID
   */
  private long bookId;
  /**
   * 书名
   */
  private String bookName;
  /**
   * 某图书的分类信息列表
   */
  private List<Category> categories;
}

编写接口

cn.lhz.mapper.BookMapper

package cn.lhz.mapper;

import cn.lhz.vo.BookVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface BookMapper {
  /**
   * 根据图书ID查询图书信息包括该图书的分类信息 直接多对多
   *
   * @param bid 图书ID
   * @return BookVo对象
   */
  BookVo selectBookVoByBid(@Param("bid") long bid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/BookMapper.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="cn.lhz.mapper.BookMapper">
  <!--根据图书ID查询图书信息包括该图书的分类信息 直接多对多-->
  <resultMap id="BookVoMap" type="bookVo">
    <id property="bookId" column="book_id"/>
    <result property="bookName" column="book_name"/>
    <collection property="categories" ofType="category">
      <id property="categoryId" column="category_id"/>
      <result property="categoryName" column="category_name"/>
    </collection>
  </resultMap>
  <select id="selectBookVoByBid" resultMap="BookVoMap">
    select b.book_id,book_name,c.category_id,c.category_name
     from book b inner join book_category bc inner join category c
     on bc.book_id = b.book_id and c.category_id = bc.category_id
     and b.book_id = #{bid};
  </select>
</mapper>
编写测试类
package cn.lhz.mapper;

import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.BookVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class BookMapperTest {

  private static final Log log = LogFactory.getLog(BookMapperTest.class);

  @Test
  void selectBookVoByBid() {
    BookMapper mapper = MyBatisUtil.getMapper(BookMapper.class, true);
    BookVo bookVo = mapper.selectBookVoByBid(3);
    MyBatisUtil.close();
    if (bookVo != null) {
      log.info(bookVo.toString());
    }
  }
}
间接多对多

多对多

编写中间表映射类

cn.lhz.pojo.StudentCourse

package cn.lhz.pojo;


import lombok.*;

import java.io.Serial;
import java.io.Serializable;

@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class StudentCourse implements Serializable {
  @Serial
  private static final long serialVersionUID = 7294808647412902387L;
  /**
   * 学生ID
   */
  private long sId;
  /**
   * 课程ID
   */
  private long cId;
  /**
   * 学生信息
   */
  private Student student;
  /**
   * 课程信息
   */
  private Course course;

}

编写接口

cn.lhz.mapper.CourseMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Course;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface CourseMapper {
  /**
   * 根据课程ID查找课程
   *
   * @param cid 课程ID
   * @return Course对象
   */
  Course selectCourseByCid(@Param("cid") long cid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/CourseMapper.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="cn.lhz.mapper.CourseMapper">
  <!--根据课程ID查找课程-->
  <select id="selectCourseByCid" resultType="course">
    select * from course where c_id = #{cid}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.CourseMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Course;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class CourseMapperTest {

  private static final Log log = LogFactory.getLog(CourseMapperTest.class);

  @Test
  void selectCourseByCid() {
    CourseMapper mapper = MyBatisUtil.getMapper(CourseMapper.class, true);
    Course course = mapper.selectCourseByCid(3);
    MyBatisUtil.close();
    if (course != null) {
      log.info(course);
    }
  }
}
编写接口

cn.lhz.mapper.StudentMapper

package cn.lhz.mapper;

import cn.lhz.vo.StudentVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface StudentMapper {
  /**
   * 根据学生ID 查询学生信息 包含所学课程列表
   *
   * @param sid 学生ID
   * @return StudentVo对象
   */
  StudentVo selectStudentVoBySid(@Param("sid") long sid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/StudentMapper.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="cn.lhz.mapper.StudentMapper">
  <!--根据学生ID 查询学生信息 包含所需课程的列表-->
  <resultMap id="StudentVoMap" type="studentVo">
    <id property="sId" column="sid"/>
    <result property="sName" column="s_name"/>
    <collection property="studentCourses" ofType="studentCourse">
      <id property="sId" column="sid"/>
      <id property="cId" column="c_id"/>
      <association property="course" column="c_id" select="cn.lhz.mapper.CourseMapper.selectCourseByCid"/>
    </collection>
  </resultMap>
  <select id="selectStudentVoBySid" resultMap="StudentVoMap">
    select stu.s_id as sid,s_name,c_id from student as stu inner join student_course sc
    on stu.s_id = sc.s_id and stu.s_id = #{sid}
  </select>
</mapper>
编写测试类

cn.lhz.mapper.StudentMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.StudentVo;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class StudentMapperTest {

  private static final Log log = LogFactory.getLog(StudentMapperTest.class);

  @Test
  void selectStudentVoBySid() {
    StudentMapper mapper = MyBatisUtil.getMapper(StudentMapper.class, true);
    StudentVo studentVo = mapper.selectStudentVoBySid(1);
    MyBatisUtil.close();
    if (studentVo != null) {
      log.info(studentVo);
    }
  }
}
SQL解析
==>  Preparing: SELECT s.s_id, s_name, c_id FROM `student` s INNER JOIN `student_course` sc ON s.s_id = sc.s_id AND s.s_id = ?
==> Parameters: 1(Long)
<==    Columns: s_id, s_name, c_id
<==        Row: 1, stu01, 1
====>  Preparing: SELECT * FROM `course` WHERE `c_id` = ?
====> Parameters: 1(Integer)
<====    Columns: c_id, c_name
<====        Row: 1, java
<====      Total: 1
<==        Row: 1, stu01, 2
====>  Preparing: SELECT * FROM `course` WHERE `c_id` = ?
====> Parameters: 2(Integer)
<====    Columns: c_id, c_name
<====        Row: 2, scala
<====      Total: 1
<==      Total: 2
编写接口

cn.lhz.mapper.StudentMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Student;
import cn.lhz.vo.StudentVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/11 下午3:08
 */
public interface StudentMapper {
    /**
     * 根据学生ID 查询学生信息 包含所需课程的ID列表
     *
     * @param sid
     * @return
     */
    StudentVo selectStudentVoBySid(@Param("sid") long sid);

    /**
     * 根据学生ID 查询学生信息
     *
     * @param sid
     * @return
     */
    Student selectStudentBySid(@Param("sid") long sid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/StudentMapper.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="cn.lhz.mapper.StudentMapper">
    <!--根据学生ID 查询学生信息 包含所需课程的ID列表-->
    <resultMap id="StudentVoMap" type="studentVo">
        <id property="sId" column="sid"/>
        <result property="sName" column="s_name"/>
        <collection property="studentCourses" ofType="studentCourse">
            <result property="sId" column="sid"/>
            <result property="cId" column="c_id"/>
            <association property="course" column="c_id" select="cn.lhz.mapper.CourseMapper.selectCourseByCid"/>
        </collection>
    </resultMap>
    <select id="selectStudentVoBySid" resultMap="StudentVoMap">
        select stu.s_id as sid, s_name, c_id
        from student as stu
                 inner join student_course sc on stu.s_id = sc.s_id and stu.s_id = #{sid}

    </select>

    <!--根据学生ID 查询学生信息-->
    <select id="selectStudentBySid" resultType="student">
        SELECT * FROM `student` WHERE `s_id` = #{sid}
    </select>
</mapper>
编写测试类

cn.lhz.mapper.StudentMapperTest

package cn.lhz.mapper;

import cn.lhz.pojo.Student;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class StudentMapperTest {

  private static final Log log = LogFactory.getLog(StudentMapperTest.class);

  @Test
  void selectStudentBySid() {
    StudentMapper mapper = MyBatisUtil.getMapper(StudentMapper.class, true);
    Student student = mapper.selectStudentBySid(3);
    MyBatisUtil.close();
    if (student != null) {
      log.info(student);
    }
  }
}
编写VO类

cn.lhz.vo.CourseVo

package cn.lhz.vo;

import cn.lhz.pojo.StudentCourse;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/14 下午12:22
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CourseVo implements Serializable {
    private static final long serialVersionUID = -2649136027724661116L;
    /**
     * 课程ID
     */
    private long cId;
    /**
     * 课程名称
     */
    private String cName;
    /**
     * 课程关联信息
     */
    private List<StudentCourse> studentCourses;
}

编写接口

cn.lhz.mapper.CourseMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Course;
import cn.lhz.vo.CourseVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/11 下午3:04
 */
public interface CourseMapper {
    /**
     * 根据课程ID查找课程
     *
     * @param cid
     * @return
     */
    Course selectCourseByCid(@Param("cid") long cid);

    /**
     * 根据课程ID查找课程 包含所需课程的ID列表
     *
     * @param cid
     * @return
     */
    CourseVo selectCourseVoByCid(@Param("cid") long cid);
}

编写映射配置文件

src/main/resources/cn/lhz/mapper/CourseMapper.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="cn.lhz.mapper.CourseMapper">
    <!--根据课程ID查找课程-->
    <select id="selectCourseByCid" resultType="course">
        SELECT *
        FROM `course`
        WHERE `c_id` = #{cid}
    </select>

    <!--根据课程ID查找课程 包含所需课程的ID列表-->
    <resultMap id="CourseVoMap" type="courseVo">
        <id property="cId" column="cid"/>
        <result property="cName" column="c_name"/>
        <collection property="studentCourses" ofType="studentCourse">
            <result property="sId" column="sid"/>
            <result property="cId" column="c_id"/>
            <association property="student" column="s_id" select="cn.lhz.mapper.StudentMapper.selectStudentBySid"/>
        </collection>
    </resultMap>
    <select id="selectCourseVoByCid" resultMap="CourseVoMap">
        select c.c_id as cid, c_name, s_id
        from course as c
                 inner join student_course sc on c.c_id = sc.c_id and c.c_id = #{cid}
    </select>
</mapper>
编写测试类

cn.lhz.mapper.CourseMapperTest

package cn.lhz.mapper;

import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.CourseVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
class CourseMapperTest {

  private static final Log log = LogFactory.getLog(CourseMapperTest.class);

  @Test
  void selectCourseVoByCid() {
    CourseMapper mapper = MyBatisUtil.getMapper(CourseMapper.class, true);
    CourseVo courseVo = mapper.selectCourseVoByCid(3);
    MyBatisUtil.close();
    if (courseVo != null) {
      log.info(courseVo);
    }
  }
}

延迟加载

@Test
void selectStudentVoBySid01() {
    StudentMapper mapper = MyBatisUtil.getMapper(StudentMapper.class, true);
    StudentVo studentVo = mapper.selectStudentVoBySid(1);
    MyBatisUtil.close();
}
Opening JDBC Connection
Created connection 226690498.
==>  Preparing: select stu.s_id as sid,s_name,c_id from student as stu inner join student_course sc on stu.s_id = sc.s_id and stu.s_id = ?
==> Parameters: 1(Long)
<==    Columns: sid, s_name, c_id
<==        Row: 1, stu01, 1
====>  Preparing: select * from course where c_id = ?
====> Parameters: 1(Long)
<====    Columns: c_id, c_name
<====        Row: 1, java
<====      Total: 1
<==        Row: 1, stu01, 2
====>  Preparing: select * from course where c_id = ?
====> Parameters: 2(Long)
<====    Columns: c_id, c_name
<====        Row: 2, scala
<====      Total: 1
<==      Total: 2
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@d8305c2]
Returned connection 226690498 to pool.

Process finished with exit code 0

@Test
void selectStudentVoBySid02() {
StudentMapper mapper = MyBatisUtil.getMapper(StudentMapper.class, true);
    StudentVo studentVo = mapper.selectStudentVoBySid(1);
    MyBatisUtil.close();
    if (studentVo != null) {
      log.info(studentVo.getSName());
    }
}
Opening JDBC Connection
Created connection 226690498.
==>  Preparing: select stu.s_id as sid,s_name,c_id from student as stu inner join student_course sc on stu.s_id = sc.s_id and stu.s_id = ?
==> Parameters: 1(Long)
<==    Columns: sid, s_name, c_id
<==        Row: 1, stu01, 1
====>  Preparing: select * from course where c_id = ?
====> Parameters: 1(Long)
<====    Columns: c_id, c_name
<====        Row: 1, java
<====      Total: 1
<==        Row: 1, stu01, 2
====>  Preparing: select * from course where c_id = ?
====> Parameters: 2(Long)
<====    Columns: c_id, c_name
<====        Row: 2, scala
<====      Total: 1
<==      Total: 2
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@d8305c2]
Returned connection 226690498 to pool.
cn.lhz.mapper.StudentMapperTest selectStudentVoBySid02
信息: stu01

Process finished with exit code 0
@Test
void selectStudentVoBySid03() {
    StudentMapper mapper = MyBatisUtil.getMapper(StudentMapper.class, true);
    StudentVo studentVo = mapper.selectStudentVoBySid(1);
    MyBatisUtil.close();
     if (studentVo != null) {
       List<StudentCourse> studentCourses = studentVo.getStudentCourses();
       studentCourses.forEach(System.out::println);
     }
}
Opening JDBC Connection
Created connection 226690498.
==>  Preparing: select stu.s_id as sid,s_name,c_id from student as stu inner join student_course sc on stu.s_id = sc.s_id and stu.s_id = ?
==> Parameters: 1(Long)
<==    Columns: sid, s_name, c_id
<==        Row: 1, stu01, 1
====>  Preparing: select * from course where c_id = ?
====> Parameters: 1(Long)
<====    Columns: c_id, c_name
<====        Row: 1, java
<====      Total: 1
<==        Row: 1, stu01, 2
====>  Preparing: select * from course where c_id = ?
====> Parameters: 2(Long)
<====    Columns: c_id, c_name
<====        Row: 2, scala
<====      Total: 1
<==      Total: 2
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@d8305c2]
Returned connection 226690498 to pool.
StudentCourse(sId=1, cId=1, student=null, course=Course(cId=1, cName=java))
StudentCourse(sId=1, cId=2, student=null, course=Course(cId=2, cName=scala))

Process finished with exit code 0

从上面第一个案例可以看出客户只想查看学生休息并不想看学生所学专业信息 但是 仍然执行了子查询,

我们希望 当想看该学生和该学生所学专业详细信息的之后才走子查询而只看学生信息的时候不走子查询

于是我们开启了延迟加载

核心配置文件

<!-- 延迟加载 -->
<!-- lazyLoadingEnabled	 延迟加载总开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--
 true	侵入式延迟  访问主对象及主对象里面的属性时,不光会加载主对象(即从数据库中查询主对象的信息),还会一同加载关联对象。 积极加载
 false	深度延迟    访问主对象属性时,只加载主,只有当访问关联对象的属性时,才会去加载关联对象。 按需加载
 默认值为false
-->
<!--<setting name="aggressiveLazyLoading" value="true"/>-->
<!--<setting name="aggressiveLazyLoading" value="false"/>-->
<!--
	fetchType="lazy"
	lazy 支持懒加载
	eager 不支持懒加载
-->
<!--collection property="empList" ofType="emp" column="dept_id" select="cn.lhz.mapper.EmpMapper.selectByDid" fetchType="lazy"/>-->
<!--<collection property="empList" ofType="emp" column="dept_id" select="cn.lhz.mapper.EmpMapper.selectByDid" fetchType="eager"/>-->
编写接口

cn.lhz.mapper.EmpMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Emp;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface EmpMapper {
  /**
   * 根据员工ID获取员工信息
   *
   * @param eid 员工ID
   * @return Emp对象
   */
  Emp selectById(@Param("eid") long eid);

  /**
   * 根据部门ID获取某部门的员工列表
   *
   * @param did 部门ID
   * @return Emp列表
   */
  List<Emp> selectByDid(@Param("did") long did);
}

编写映射配置文件

cn/lhz/mapper/EmpMapper.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="cn.lhz.mapper.EmpMapper">
  <!--根据员工ID获取员工信息-->
  <select id="selectById" resultType="emp">
    select * from emp where emp_id = #{eid}
  </select>

  <!--根据部门ID获取某部门的员工列表-->
  <select id="selectByDid" resultType="emp">
    select * from emp where dept_id = #{did}
  </select>
</mapper>
编写VO类

cn.lhz.vo.DeptVo

package cn.lhz.vo;

import cn.lhz.pojo.Emp;
import lombok.*;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class DeptVo {
  /**
   * 部门编号
   */
  private long deptId;
  /**
   * 部门名称
   */
  private String deptName;
  /**
   * 员工列表
   */
  private List<Emp> empList;
}

编写接口

cn.lhz.mapper.DeptMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import cn.lhz.vo.DeptVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface DeptMapper {
  /**
   * 根据部门ID获取该部门信息包含员工列表
   *
   * @param did 部门ID
   * @return DeptVo对象
   */
  DeptVo selectDeptVoById(@Param("did") long did);
}

编写映射配置文件
<?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="cn.lhz.mapper.DeptMapper">
  <!--根据部门ID获取该部门信息包含员工列表-->
  <resultMap id="DeptVoMap" type="deptVo">
    <id property="deptId" column="dept_id"/>
    <result property="deptName" column="dept_name"/>
    <collection property="empList" ofType="emp" column="dept_id" select="cn.lhz.mapper.EmpMapper.selectByDid" fetchType="lazy"/>
    <!--<collection property="empList" ofType="emp" column="dept_id" select="cn.lhz.mapper.EmpMapper.selectByDid" fetchType="eager"/>-->
  </resultMap>
  <select id="selectDeptVoById" resultMap="DeptVoMap">
    select * from dept where dept_id = #{did}
  </select>
</mapper>
编写测试类
package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.DeptVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class DeptMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);
  /**
   * 延迟加载
   */
  @Test
  void selectDeptVoById01() {
    DeptMapper mapper = MyBatisUtil.getMapper(DeptMapper.class, true);
    DeptVo deptVo = mapper.selectDeptVoById(1);
    MyBatisUtil.close();
  }
}

<setting name="aggressiveLazyLoading" value="true"/>
Opening JDBC Connection
Created connection 812143047.
==>  Preparing: select * from dept where dept_id = ?
==> Parameters: 1(Long)
<==    Columns: dept_id, dept_name
<==        Row: 1, 开发部
<==      Total: 1

Process finished with exit code 0
<setting name="aggressiveLazyLoading" value="false"/>
Opening JDBC Connection
Created connection 812143047.
==>  Preparing: select * from dept where dept_id = ?
==> Parameters: 1(Long)
<==    Columns: dept_id, dept_name
<==        Row: 1, 开发部
<==      Total: 1

Process finished with exit code 0
package cn.lhz.mapper;

import cn.lhz.pojo.Dept;
import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.DeptVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;


/**
 * @author 李昊哲
 * @version 1.0.0
 */
class DeptMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  /**
   * 延迟加载
   */
  @Test
  void selectDeptVoById02() {
    DeptMapper mapper = MyBatisUtil.getMapper(DeptMapper.class, true);
    DeptVo deptVo = mapper.selectDeptVoById(1);
    MyBatisUtil.close();
    String deptName = deptVo.getDeptName();
  }
}

<setting name="aggressiveLazyLoading" value="true"/>
Opening JDBC Connection
Created connection 812143047.
==>  Preparing: select * from dept where dept_id = ?
==> Parameters: 1(Long)
<==    Columns: dept_id, dept_name
<==        Row: 1, 开发部
<==      Total: 1
==>  Preparing: select * from emp where dept_id = ?
==> Parameters: 1(Long)
<==    Columns: emp_id, emp_name, dept_id
<==        Row: 1, 戴撵, 1
<==        Row: 4, 贾定, 1
<==        Row: 5, 何乌暇, 1
<==        Row: 7, 雷仗, 1
<==        Row: 9, 龚跪苹, 1
<==        Row: 10, 西门伍锤, 1
<==        Row: 15, 白佰耐, 1
<==        Row: 16, 郭访, 1
<==        Row: 21, 冯幸, 1
<==        Row: 22, 龚墟, 1
<==        Row: 26, 方坯身, 1
<==        Row: 31, 严敞签, 1
<==        Row: 37, 孙高团, 1
<==        Row: 38, 武朵剑, 1
<==        Row: 41, 赵废酣, 1
<==        Row: 45, 赵涎, 1
<==        Row: 48, 李滴, 1
<==        Row: 51, 康掠, 1
<==        Row: 52, 薛逛, 1
<==        Row: 58, 谢军感, 1
<==        Row: 66, 轩辕难虞, 1
<==        Row: 72, 皇甫铸, 1
<==        Row: 73, 许陵伎, 1
<==        Row: 84, 陆枣沽, 1
<==        Row: 85, 皇甫府靡, 1
<==        Row: 86, 金淘, 1
<==        Row: 87, 杜躁, 1
<==        Row: 92, 於急, 1
<==        Row: 94, 石扒呀, 1
<==        Row: 95, 卢靛徽, 1
<==        Row: 102, 方安愈, 1
<==        Row: 104, 姚久负, 1
<==        Row: 106, 万鼻, 1
<==        Row: 109, 钱汞指, 1
<==        Row: 116, 江风椭, 1
<==        Row: 117, 孙郝眩, 1
<==        Row: 118, 上官竖, 1
<==        Row: 119, 冯滩姑, 1
<==        Row: 120, 何谬, 1
<==        Row: 123, 任碾, 1
<==        Row: 128, 郑握编, 1
<==        Row: 129, 诸葛颗扭, 1
<==        Row: 130, 卢抢, 1
<==        Row: 132, 龚隅, 1
<==        Row: 133, 洪阜猾, 1
<==        Row: 140, 陆汕, 1
<==        Row: 142, 郑陡枉, 1
<==        Row: 147, 公孙场捌, 1
<==        Row: 151, 贾蔚犁, 1
<==        Row: 152, 尉迟候, 1
<==        Row: 153, 夏志咎, 1
<==        Row: 155, 欧阳碍, 1
<==        Row: 157, 皇甫皱檄, 1
<==        Row: 161, 赵撼搪, 1
<==        Row: 162, 韩栓, 1
<==        Row: 163, 田郑, 1
<==        Row: 165, 萧轨, 1
<==        Row: 167, 苏篇, 1
<==        Row: 171, 宋隙控, 1
<==        Row: 172, 程旋兵, 1
<==        Row: 174, 薛壹攒, 1
<==        Row: 176, 邹堂, 1
<==        Row: 177, 龚啸, 1
<==        Row: 178, 武盯钢, 1
<==        Row: 179, 牛葵, 1
<==        Row: 181, 沈袍, 1
<==        Row: 184, 邱渺拇, 1
<==        Row: 192, 龙藤, 1
<==      Total: 68

Process finished with exit code 0

<setting name="aggressiveLazyLoading" value="fasle"/>
Opening JDBC Connection
Created connection 812143047.
==>  Preparing: select * from dept where dept_id = ?
==> Parameters: 1(Long)
<==    Columns: dept_id, dept_name
<==        Row: 1, 开发部
<==      Total: 1

Process finished with exit code 0

侵入式延迟 积极加载

侵入式延迟 访问主对象及主对象里面的属性时,不光会加载主对象(即从数据库中查询主对象的信息),还会一同加载关联对象。 积极加载

深度延迟 访问主对象属性时,只加载主,只有当访问关联对象的属性时,才会去加载关联对象。 按需加载

<!-- 延迟加载 -->
<!-- lazyLoadingEnabled	 延迟加载总开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--
 true	侵入式延迟  访问主对象及主对象里面的属性时,不光会加载主对象(即从数据库中查询主对象的信息),还会一同加载关联对象。 积极加载
 false	深度延迟    访问主对象属性时,只加载主,只有当访问关联对象的属性时,才会去加载关联对象。 按需加载
 默认值为false
-->
<!--<setting name="aggressiveLazyLoading" value="true"/>-->
<!--<setting name="aggressiveLazyLoading" value="false"/>-->
<!--
	fetchType="lazy"
	lazy 支持懒加载
	eager 不支持懒加载
-->
<!--collection property="empList" ofType="emp" column="dept_id" select="cn.lhz.mapper.EmpMapper.selectByDid" fetchType="lazy"/>-->
<!--<collection property="empList" ofType="emp" column="dept_id" select="cn.lhz.mapper.EmpMapper.selectByDid" fetchType="eager"/>-->

映射配置文件中子查询设置fetchType

  • fetchType=“eager” 不支持懒加载

  • fetchType=“lzsy” 支持懒加载

一级缓存

编写测试类

cn.lhz.mapper.CacheTest

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * mybatis一级缓存默认开启,同一个SqlSession中查询条件一致则走一级缓存,一级缓存不能跨SqlSession
 * @author 李昊哲
 * @version 1.0.0
 */
public class CacheTest {
  @Test
  public void testCache01() {
    PersonMapper mapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person01 = mapper.selectById(3);
    Person person02 = mapper.selectById(3);
    MyBatisUtil.close();
  }
}

Opening JDBC Connection
Created connection 1552870927.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
==> Parameters: 3(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 3, 5ee0074d47064285b8c5925534d70d84, 17674856547, 令狐荡, 649152199504171242
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Returned connection 1552870927 to pool.
package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * mybatis一级缓存默认开启,同一个SqlSession中查询条件一致则走一级缓存,一级缓存不能跨SqlSession
 * @author 李昊哲
 * @version 1.0.0
 */
public class CacheTest {
    @Test
  public void testCache02() {
    PersonMapper mapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person01 = mapper.selectById(1);
    Person person02 = mapper.selectById(3);
    MyBatisUtil.close();
  }
}

Opening JDBC Connection
Created connection 1552870927.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
==> Parameters: 6(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 6, aebe1bd701b24fb89347c78bb3aaf938, 13651227685, 欧阳脓, 644102199803081660
<==      Total: 1
==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
==> Parameters: 3(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 3, 5ee0074d47064285b8c5925534d70d84, 17674856547, 令狐荡, 649152199504171242
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Returned connection 1552870927 to pool.
package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * mybatis一级缓存默认开启,同一个SqlSession中查询条件一致则走一级缓存,一级缓存不能跨SqlSession
 * @author 李昊哲
 * @version 1.0.0
 */
public class CacheTest {
    @Test
  public void testCache03() {
    PersonMapper mapper01 = MyBatisUtil.getMapper(PersonMapper.class, true);
    PersonMapper mapper02 = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person01 = mapper01.selectById(3);
    Person person02 = mapper02.selectById(3);
    MyBatisUtil.close();
  }
}

Opening JDBC Connection
Created connection 1552870927.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
==> Parameters: 3(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 3, 5ee0074d47064285b8c5925534d70d84, 17674856547, 令狐荡, 649152199504171242
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Returned connection 1552870927 to pool.
package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * mybatis一级缓存默认开启,同一个SqlSession中查询条件一致则走一级缓存,一级缓存不能跨SqlSession
 * @author 李昊哲
 * @version 1.0.0
 */
public class CacheTest {
  @Test
  public void testCache04() {
    PersonMapper mapper01 = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person01 = mapper01.selectById(3);
    MyBatisUtil.close();

    PersonMapper mapper02 = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person02 = mapper02.selectById(3);
    MyBatisUtil.close();
  }
}

Opening JDBC Connection
Created connection 1552870927.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
==> Parameters: 3(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 3, 5ee0074d47064285b8c5925534d70d84, 17674856547, 令狐荡, 649152199504171242
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Returned connection 1552870927 to pool.
Opening JDBC Connection
Checked out connection 1552870927 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
==> Parameters: 3(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 3, 5ee0074d47064285b8c5925534d70d84, 17674856547, 令狐荡, 649152199504171242
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c8eee0f]
Returned connection 1552870927 to pool.

结论:mybatis一级缓存默认开启,同一个SqlSession中查询条件一致则走一级缓存,一级缓存不能跨SqlSession

二级缓存

MyBatis如果想跨SqlSession缓存则可以采用将查询结果缓存至第三方,例如:

  • ehcache
  • memcached
  • redis

开启二级缓存步骤

maven项目提前在pom.xml文件中增加相关依赖

  1. 在核心配置文件中加入
<settings>
		<setting name="cacheEnabled" value="true" />
</settings>
  1. 在映射配置文件中加入
    回收策略:
    LRU – 最近最少使用的:移除最长时间不被使用的对象。
    FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
  1. 缓存遇到commit失效

  2. ehcache二级缓存在映射配置文件中加入

<cache type="org.mybatis.caches.ehcache.LoggingEhcache" />
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

添加maven依赖

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.10.8</version>
</dependency>
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.3</version>
</dependency>

编写核心配置文件

<settings>
	<!--开启二级缓存-->
	<setting name="cacheEnabled" value="true" />
</settings>

编写映射配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.xml

添加以下内容

<!--
	flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

	size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

	readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提	升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />

在需要缓存的查找标签增加设置useCache=true

<?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="cn.lhz.mapper.PersonMapper">
  <!--
    flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
    size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
    readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提	升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
    -->
  <cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/>
  <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>
  <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

  <!--定义SQL片段-->
  <sql id="select_person">
    SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card`
    FROM `person`
  </sql>

  <!--根据用户ID查询该用户信息 使用SQL片段-->
  <select id="selectById" resultType="person" useCache="true">
    <include refid="select_person"/>
    where id = #{id};
  </select>
</mapper>

编写测试类

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.util.mybatis.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * mybatis一级缓存默认开启,同一个SqlSession中查询条件一致则走一级缓存,一级缓存不能跨SqlSession
 * @author 李昊哲
 * @version 1.0.0
 */
public class CacheTest {
  @Test
  public void testCache04() {
    PersonMapper mapper01 = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person01 = mapper01.selectById(3);
    MyBatisUtil.close();

    PersonMapper mapper02 = MyBatisUtil.getMapper(PersonMapper.class, true);
    Person person02 = mapper02.selectById(3);
    MyBatisUtil.close();
  }
}

Cache Hit Ratio [cn.lhz.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Created connection 1037955032.
==>  Preparing: SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` where id = ?;
==> Parameters: 3(Long)
<==    Columns: id, uuid, mobile, nickname, id_card
<==        Row: 3, 1fca11ac067d4a5d8b8e7b8d1904148a, 13925135946, 崔障镜, 651622198510173550
<==      Total: 1
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3dddefd8]
Returned connection 1037955032 to pool.
Cache Hit Ratio [cn.lhz.mapper.PersonMapper]: 0.5

Process finished with exit code 0

分页插件 pagehelper

引入maven依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>6.1.0</version>
</dependency>

完整pom.xml文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.lhz</groupId>
  <artifactId>mybatis</artifactId>
  <version>1.0.0</version>
  <name>mybatis</name>
  <packaging>jar</packaging>

  <properties>
    <jdk.version>21</jdk.version>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <maven.compiler.compilerVersion>21</maven.compiler.compilerVersion>
    <maven.compiler.encoding>utf-8</maven.compiler.encoding>
    <project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <maven.test.failure.ignore>true</maven.test.failure.ignore>
    <maven.test.skip>true</maven.test.skip>
    <junit.version>5.10.2</junit.version>
    <commons-dbutils.version>1.8.1</commons-dbutils.version>
    <commons-io.version>2.16.1</commons-io.version>
    <commons-lang3.version>3.14.0</commons-lang3.version>
    <dom4j.version>2.1.4</dom4j.version>
    <druid.version>1.2.22</druid.version>
    <fastjson.version>2.0.49</fastjson.version>
    <hibernate.version>6.5.1.Final</hibernate.version>
    <hutool.version>5.8.27</hutool.version>
    <generator.version>1.1.2</generator.version>
    <guava.version>33.0.0-jre</guava.version>
    <gson.version>2.10.1</gson.version>
    <jackson.version>2.17.0</jackson.version>
    <jaxen.version>2.0.0</jaxen.version>
    <lombok.version>1.18.32</lombok.version>
    <mybatis.version>3.5.16</mybatis.version>
    <mysql.version>8.4.0</mysql.version>
    <spring.version>6.1.6</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>RELEASE</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.github.binarywang</groupId>
      <artifactId>java-testdata-generator</artifactId>
      <version>${generator.version}</version>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>${hutool.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>${commons-io.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>${commons-lang3.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>${gson.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson.version}</version>
    </dependency>
    <!--jackson-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
    <dependency>
      <groupId>org.dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>${dom4j.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/jax33.0.0-jreen/jaxen -->
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>${jaxen.version}</version>
    </dependency>
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>${guava.version}</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${druid.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-dbutils</groupId>
      <artifactId>commons-dbutils</artifactId>
      <version>${commons-dbutils.version}</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>
    <dependency>
      <groupId>org.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>3.10.8</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-ehcache</artifactId>
      <version>1.2.3</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>6.1.0</version>
    </dependency>
  </dependencies>
  <build>
    <!--项目打包文件名-->
    <finalName>${project.name}</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.4.0</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.4.1</version>
      </plugin>
      <!-- 编译级别 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
        <configuration>
          <!-- 设置编译字符编码 -->
          <encoding>UTF-8</encoding>
          <!-- 设置编译jdk版本 -->
          <source>21</source>
          <target>21</target>
          <compilerArgs>--enable-preview</compilerArgs>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.3.2</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.3.1</version>
      </plugin>
      <!-- 打包的时候跳过测试junit begin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <!--<version>2.22.2</version>-->
        <version>3.2.5</version>
        <configuration>
          <skip>true</skip>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

编写核心配置文件

追加以下内容

<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
      <!-- 方言 -->
      <property name="helperDialect" value="mysql"/>
      <!--
        默认值为 false,当该参数设置为 true 时, 如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果
        (相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
      -->
      <property name="pageSizeZero" value="true"/>
      <!--
        分页合理化参数,默认值为false。
        当该参数设置为 true 时, pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。
        默认false 时,直接根据参数进行查询
      -->
      <property name="reasonable" value="true"/>
    </plugin>
</plugins>

page参数详解

1. helperDialect :分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置 helperDialect 属性来指定分页插件使用哪种方言。
	配置时,可以使用下面的缩写值:
	oracle , mysql , mariadb , sqlite , hsqldb , postgresql , db2 , sqlserver , informix , h2 , sqlserver2012 , derby
	特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012 ,否则会使用 SqlServer2005 的方式进行分页。 你也可以实现 			AbstractHelperDialect ,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。

2. offsetAsPageNum :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。

3. rowBoundsWithCount :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。当该参数设置为 true 时,使用 RowBounds 分页会进行 count 查询。   

4. pageSizeZero :默认值为 false ,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。

5. reasonable :分页合理化参数,默认值为 false 。当该参数设置为 true 时, pageNum<=0 时会查询第一页, pageNum>pages (超过总数时),会查询最后一页。默认 false 时,直接根据参数进行查询。

6. params :为了支持 startPage(Object params) 方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable ,不配置映射的用默认值, 默认值为 pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero= pageSizeZero 。

7. supportMethodsArguments :支持通过 Mapper 接口参数来传递分页参数,默认值 false ,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest 。

8. autoRuntimeDialect :默认值为 false 。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择 sqlserver2012 ,只能使用 sqlserver ),用法和注意事项参考下面的场景五。

9. closeConn :默认值为 true 。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认 true 关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。

完整核心配置文件

<?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>
  <!--
      这些属性可以在外部进行配置,并可以进行动态替换。
      你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
  -->
  <properties resource="config.properties"/>
  <settings>
    <!-- 驼峰命名与下划线自动转换 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- log日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    <!-- lazyLoadingEnabled	 延迟加载总开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--
     true	侵入式延迟  访问主对象及主对象里面的属性时,不光会加载主对象(即从数据库中查询主对象的信息),还会一同加载关联对象。 积极加载
     false	深度延迟    访问主对象属性时,只加载主,只有当访问关联对象的属性时,才会去加载关联对象。 按需加载
     默认值为false
    -->
    <!--<setting name="aggressiveLazyLoading" value="true"/>-->
    <!--<setting name="aggressiveLazyLoading" value="false"/>-->
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
  </settings>
  <typeAliases>
    <!--
		别名
		type	类的完全限定名
		alias	别名
    -->
    <!--为某个类指定别名-->
    <!--<typeAlias alias="person" type="cn.lhz.pojo.Person"/>-->
    <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
    <package name="cn.lhz.pojo"/>
    <package name="cn.lhz.vo"/>
  </typeAliases>
  <plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
      <!-- 方言 -->
      <property name="helperDialect" value="mysql"/>
      <!--
        默认值为 false,当该参数设置为 true 时, 如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果
        (相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
      -->
      <property name="pageSizeZero" value="true"/>
      <!--
        分页合理化参数,默认值为false。
        当该参数设置为 true 时, pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。
        默认false 时,直接根据参数进行查询
      -->
      <property name="reasonable" value="true"/>
    </plugin>
  </plugins>
  <!--
     MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
     例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
  -->
  <!--默认使用的环境 ID(比如:default="development")。-->
  <environments default="development">
    <!--每个 environment 元素定义的环境 ID(比如:id="development")。-->
    <environment id="development">
      <!--事务管理器的配置(比如:type="JDBC")。-->
      <!--
          事务管理器(transactionManager)
          在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
          JDBC – 这个配置直接使用了 JDBC 的提交和回滚设置,它依赖从数据源获得的连接来管理事务作用域。
          MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
          默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
          如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
          这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
      -->
      <transactionManager type="JDBC"/>
      <!--数据源的配置(比如:type="POOLED")-->
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!--
   既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。
   但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
   在自动查找资源方面,Java 并没有提供一个很好地解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
   你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。
  -->
  <mappers>
    <!-- 使用相对于类路径的资源引用 -->
    <!--单独加载某个映射配置文件-->
    <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
    <!-- 使用完全限定资源定位符(URL) -->
    <!--<mapper url="file:///home/lhz/Documents/code/mybatis/src/main/resources/mybatis/mapper/PersonMapper.xml"/>-->
    <!--<mapper url="file:///D:/mybatis/src/main/resources/cn/lhz/mapper/PersonMapper.xml"/>-->
    <!--加载某包下所有的映射配置文件-->
    <package name="cn.lhz.mapper"/>
  </mappers>
</configuration>

编写接口

cn.lhz.mapper.PersonMapper

package cn.lhz.mapper;

import cn.lhz.pojo.Person;
import cn.lhz.vo.FatherVo;
import cn.lhz.vo.PersonVo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public interface PersonMapper {
  /**
   * 分页查询
   *
   * @param person 查询条件
   * @param gender 性别
   * @return Person列表
   */
  List<Person> select4page(@Param("person") Person person, @Param("gender") Integer gender);
}

编写映射的配置文件

src/main/resources/cn/lhz/mapper/PersonMapper.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	命名空间	对应接口的名字
-->
<mapper namespace="cn.lhz.mapper.PersonMapper">
  <!--分页查询-->
  <select id="select4page" resultType="person">
    select * from person
    <trim prefix="where" prefixOverrides="AND|OR">
      <if test="gender != null and (gender == 1 or gender == 0)">
        and if(mod(substr(id_card,17,1),2),1,0) = #{gender}
      </if>
      <if test="person.nickname != null and person.nickname.length &gt; 0">
        <bind name="like_nickname" value="'%' + person.nickname + '%'"/>
        and `nickname` like #{like_nickname}
      </if>
    </trim>
  </select>
</mapper>

编写测试类

cn.lhz.mapper.PersonMapperTest

package cn.lhz.mapper;

import cn.binarywang.tools.generator.ChineseIDCardNumberGenerator;
import cn.binarywang.tools.generator.ChineseMobileNumberGenerator;
import cn.binarywang.tools.generator.ChineseNameGenerator;
import cn.hutool.core.util.IdUtil;
import cn.lhz.pojo.Person;
import cn.lhz.util.json.jackson.JacksonUtils;
import cn.lhz.util.mybatis.MyBatisUtil;
import cn.lhz.vo.PersonVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0
 */
public class PersonMapperTest {
  private static final Log log = LogFactory.getLog(PersonMapperTest.class);

  @Test
  void select4page() {
    // 1. 获取持久层代理对象
    PersonMapper mapper = MyBatisUtil.getMapper(PersonMapper.class, true);
    // 2. 在执行代理对象中查询方法之前 开启分页
    PageHelper.startPage(1, 5);
    // 准备查询条件
    Person person = new Person();
    // person.setNickname("李");
    // 开启分页后 执行代理对象中的查询方法
    // List<Person> personList = mapper.select4page(person, null);
    List<Person> personList = mapper.select4page(person, 1);
    MyBatisUtil.close();
    if (!personList.isEmpty()) {
      personList.forEach(System.out::println);
    }
    // 封装分页数据
    PageInfo<Person> pageInfo = new PageInfo<>(personList);
    System.out.println(JacksonUtils.bean2json(pageInfo));
  }
}

PageInfo类属性介绍

属性名属性说明
pageNum当前页
pageSize每页的数量
size当前页的数量
startRow由于startRow和endRow不常用,这里说个具体的用法 可以在页面中"显示startRow到endRow 共size条数据" 当前页面第一个元素在数据库中的行号
endRow
pages总页数
prePage前一页
nextPage下一页
isFirstPage是否为第一页
isLastPage是否为最后一页
hasPreviousPage是否有前一页
hasNextPage是否有下一页
navigatePages导航页码数
navigatepageNums所有导航页号
nnavigateFirstPage导航条上的第一页
navigateLastPage导航条上的最后一页
list数据集合
total总数量

完整核心配置文件

<?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>
  <!--
      这些属性可以在外部进行配置,并可以进行动态替换。
      你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
  -->
  <properties resource="config.properties"/>
  <settings>
    <!-- 驼峰命名与下划线自动转换 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- log日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    <!-- lazyLoadingEnabled	 延迟加载总开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--
     true	侵入式延迟  访问主对象及主对象里面的属性时,不光会加载主对象(即从数据库中查询主对象的信息),还会一同加载关联对象。 积极加载
     false	深度延迟    访问主对象属性时,只加载主,只有当访问关联对象的属性时,才会去加载关联对象。 按需加载
     默认值为false
    -->
    <!--<setting name="aggressiveLazyLoading" value="true"/>-->
    <!--<setting name="aggressiveLazyLoading" value="false"/>-->
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
  </settings>
  <typeAliases>
    <!--
		别名
		type	类的完全限定名
		alias	别名
    -->
    <!--为某个类指定别名-->
    <!--<typeAlias alias="person" type="cn.lhz.pojo.Person"/>-->
    <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
    <package name="cn.lhz.pojo"/>
    <package name="cn.lhz.vo"/>
  </typeAliases>
  <plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
      <!--
        1. helperDialect :分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置 helperDialect 属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:
            oracle , mysql , mariadb , sqlite , hsqldb , postgresql , db2 , sqlserver , informix , h2 , sqlserver2012 , derby
            特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012 ,否则会使用 SqlServer2005 的方式进行分页。 你也可以实现 AbstractHelperDialect ,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。
        2. offsetAsPageNum :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。
        3. rowBoundsWithCount :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。当该参数设置为 true 时,使用 RowBounds 分页会进行 count 查询。   
        4. pageSizeZero :默认值为 false ,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
        5. reasonable :分页合理化参数,默认值为 false 。当该参数设置为 true 时, pageNum<=0 时会查询第一页, pageNum>pages (超过总数时),会查询最后一页。默认 false 时,直接根据参数进行查询。
        6. params :为了支持 startPage(Object params) 方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable ,不配置映射的用默认值, 默认值为 pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero= pageSizeZero 。
        7. supportMethodsArguments :支持通过 Mapper 接口参数来传递分页参数,默认值 false ,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest 。
        8. autoRuntimeDialect :默认值为 false 。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择 sqlserver2012 ,只能使用 sqlserver ),用法和注意事项参考下面的场景五。
        9. closeConn :默认值为 true 。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认 true 关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。
      -->
      <!-- 方言 -->
      <property name="helperDialect" value="mysql"/>
      <!--
        默认值为 false,当该参数设置为 true 时, 如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果
        (相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
      -->
      <property name="pageSizeZero" value="true"/>
      <!--
        分页合理化参数,默认值为false。
        当该参数设置为 true 时, pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。
        默认false 时,直接根据参数进行查询
      -->
      <property name="reasonable" value="true"/>
    </plugin>
  </plugins>
  <!--
     MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
     例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
  -->
  <!--默认使用的环境 ID(比如:default="development")。-->
  <environments default="development">
    <!--每个 environment 元素定义的环境 ID(比如:id="development")。-->
    <environment id="development">
      <!--事务管理器的配置(比如:type="JDBC")。-->
      <!--
          事务管理器(transactionManager)
          在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
          JDBC – 这个配置直接使用了 JDBC 的提交和回滚设置,它依赖从数据源获得的连接来管理事务作用域。
          MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
          默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
          如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
          这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
      -->
      <transactionManager type="JDBC"/>
      <!--数据源的配置(比如:type="POOLED")-->
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!--
   既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。
   但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
   在自动查找资源方面,Java 并没有提供一个很好地解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
   你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。
  -->
  <mappers>
    <!-- 使用相对于类路径的资源引用 -->
    <!--单独加载某个映射配置文件-->
    <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
    <!-- 使用完全限定资源定位符(URL) -->
    <!--<mapper url="file:///home/lhz/Documents/code/mybatis/src/main/resources/mybatis/mapper/PersonMapper.xml"/>-->
    <!--<mapper url="file:///D:/mybatis/src/main/resources/cn/lhz/mapper/PersonMapper.xml"/>-->
    <!--加载某包下所有的映射配置文件-->
    <package name="cn.lhz.mapper"/>
  </mappers>
</configuration>
Logo

本社区面向用户介绍CSDN开发云部门内部产品使用和产品迭代功能,产品功能迭代和产品建议更透明和便捷

更多推荐