MyBatis从入门到精通_李昊哲小课-程序员宅基地

技术标签: Java  java  mybatis  mysql  数据库  Mybatis  大数据  

MyBatis

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

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

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

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

  1. 什么是ORM

    OBject Relation Mapping 对象关系映射

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

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

    ORM
    ORM
    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>com.lihaozhe</groupId>
  <artifactId>mybatis</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>mybatis</name>
  <packaging>jar</packaging>

  <properties>
    <jdk.version>17</jdk.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <maven.compiler.compilerVersion>17</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.8.2</junit.version>

    <fastjson.version>2.0.6</fastjson.version>
    <gson.version>2.9.0</gson.version>
    <hutool.version>5.8.0.M3</hutool.version>
    <jackson.version>2.13.3</jackson.version>
    <lombok.version>1.18.24</lombok.version>
    <java-testdata.version>1.1.2</java-testdata.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.github.binarywang</groupId>
      <artifactId>java-testdata-generator</artifactId>
      <version>${java-testdata.version}</version>
    </dependency>
    <!--commons-->
    <!--commons-lang3-->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.8.1</version>
    </dependency>
    <!--commons-io-->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.11.0</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>${hutool.version}</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>
    <!--json-->
    <!--fastjson-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson.version}</version>
    </dependency>
    <!--gson-->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>${gson.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>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.29</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.10</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.8</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.20</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.7.36</version>
    </dependency>
  </dependencies>

  <build>
    <!--项目打包文件名-->
    <!--<finalName>news</finalName>-->
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.3.2</version>
      </plugin>
      <!-- 编译级别 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</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.2.0</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
      <!-- 打包的时候跳过测试junit begin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
        <configuration>
          <skip>true</skip>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

根据数据表生成对应POJO类

IDEA
点击下载数据库脚本文件

编写数据源文件

src/main/resources/config.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///mybatis?useUnicode=true&characterEncoding=UTF8&useSSL=false&useServerPrepStmts=false&rewriteBatchedStatements=true&cachePrepStmts=true&allowMultiQueries=true&serverTimeZone=Aisa/Shanghai
username=root
password=123456

编写核心配置文件

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核心配置文件

测试数据库连通性

@Test
public void testConetion() 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();
    System.out.println(sqlSession.getConnection());
    // 归还连接给数据源
    sqlSession.close();
}

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/6 上午8:52
 */
public interface PersonMapper {
    
    /**
     * 查询用户列表 resultType
     *
     * @return
     */
    List<Person> selectAll4t();
}

编写映射配置文件

src/main/java/com/lihaozhe/mapper/PersonMapper.xml

或者

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

    <!--查询用户列表 resultType-->
    <select id="selectAll4t" resultType="com.lihaozhe.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/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
    <!--<mapper url="file:///D:/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
    <!--加载某包下所有的映射配置文件-->
    <package name="com.lihaozhe.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>
    <mappers>
        <!-- 使用相对于类路径的资源引用 -->
        <!--单独加载某个映射配置文件-->
        <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
        <!-- 使用完全限定资源定位符(URL) -->
        <!--<mapper url="file:///home/lhz/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--<mapper url="file:///D:/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--加载某包下所有的映射配置文件-->
        <package name="com.lihaozhe.mapper"/>
    </mappers>

</configuration>

测试

src/test/java/com/lihaozhe/mapper/PersonMapperTest.java

package com.lihaozhe.mapper;

import com.lihaozhe.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/6 上午8:52
 */
public class PersonMapperTest {
    
    private SqlSession sqlSession;

    // JUnit5 @BeforeAll和@BeforeEach注解替换了 JUnit4 中的@Before注解。
    // 它⽤于表⽰应在当前类中的每个@Test⽅法之前执⾏注解⽅法。
    // 注意:@BeforeAll注解的⽅法必须为静态⽅法,否则它将引发运⾏时错误。
    // 注意:@BeforeEach注解的⽅法不得为静态⽅法,否则它将引发运⾏时错误。
    @BeforeEach
    public void openSqlSession() 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 selectAll4t() {
    
        // 获取该接口的代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 执行接口中的方法
        List<Person> people = mapper.selectAll4t();
        // 释放SqlSession资源
        sqlSession.close();
        // 遍历集合
        people.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="com.lihaozhe.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=“com.lihaozhe.pojo.Person” 为 resultMap=“PersonMap”

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

在接口中增加对应的方法

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    
    /**
     * 查询用户列表 resultType
     *
     * @return
     */
    List<Person> selectAll4t();
    /**
     * 查询用户列表 resultMap
     *
     * @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(){
    
    // 获取该接口的代理对象
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
    // 执行接口中的方法
    List<Person> people = mapper.selectAll4t();
    // 释放SqlSession资源
    sqlSession.close();
    // 遍历集合
    people.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" />
    <settings>
        <!-- 驼峰命名与下划线自动转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- log日志 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--
        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>
    <mappers>
        <!-- 使用相对于类路径的资源引用 -->
        <!--单独加载某个映射配置文件-->
        <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
        <!-- 使用完全限定资源定位符(URL) -->
        <!--<mapper url="file:///home/lhz/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--<mapper url="file:///D:/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--加载某包下所有的映射配置文件-->
        <package name="com.lihaozhe.mapper"/>
    </mappers>

</configuration>

select

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/6 上午8:52
 */
public interface PersonMapper {
    
    /**
     * 根据手机号查询用户信息
     * @param mobile
     * @return
     */
    Person selectByMobile(@Param("mobile") String mobile);
}

编写映射配置文件

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

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

</mapper>

MyBatis

测试类

com.lihaozhe.mapper.PersonMapperTest

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

@Test
public void selectByMobile(){
    
    // 获取该接口的代理对象
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
    // 执行接口中的方法
    Person person = mapper.selectByMobile("13925135946");
    // 释放SqlSession资源
    sqlSession.close();
    // 输出查询结果
    System.out.println(person);
}

#与$的区别

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

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

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

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

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/6 上午8:52
 */
public interface PersonMapper {
    
   /**
     * 根据不确定的字段查询
     * @param column
     * @param value
     * @return
     */
    Person selectByFiled(@Param("column") String column,@Param("value")String value);
}

编写映射配置文件

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

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

测试类

com.lihaozhe.mapper.PersonMapperTest

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

@Test
public void selectByFiled() {
    
    // 获取该接口的代理对象
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
    // 执行接口中的方法
    Person person = mapper.selectByFiled("mobile", "18400880850");
    // 释放SqlSession资源
    sqlSession.close();
    // 输出查询结果
    System.out.println(person);
}

解析结果:

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

MyBatis#与$区别

别名映射

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

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

<typeAliases>
    <!--
		别名
		type	类的完全限定名
		alias	别名
    -->
    <!--为某个类指定别名-->
    <typeAlias alias="login" type="com.lihaozhe.pojo.Login"/>
</typeAliases>

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
import org.apache.ibatis.annotations.Param;

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

编写映射配置文件

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

测试类

com.lihaozhe.mapper.PersonMapperTest

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

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
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 2022/7/6 上午8:52
 */
public class LoginMapperTest {
    
    private SqlSession sqlSession;

    // JUnit5 @BeforeAll和@BeforeEach注解替换了 JUnit4 中的@Before注解。
    // 它⽤于表⽰应在当前类中的每个@Test⽅法之前执⾏注解⽅法。
    // 注意:@BeforeAll注解的⽅法必须为静态⽅法,否则它将引发运⾏时错误。
    // 注意:@BeforeEach注解的⽅法不得为静态⽅法,否则它将引发运⾏时错误。
    @BeforeEach
    public void openSqlSession() 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 selectByPid(){
    
        LoginMapper mapper = sqlSession.getMapper(LoginMapper.class);
        Login login = mapper.selectByPid(3);
        sqlSession.close();
        System.out.println(login);
    }
}

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

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

<typeAliases>
    <!--
		别名
		type	类的完全限定名
        alias	别名
	-->
    <!--为某个类指定别名-->
    <!--<typeAlias alias="person" type="com.lihaozhe.pojo.Person"/>-->
    <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
    <package name="com.lihaozhe.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" />
    <settings>
        <!-- 驼峰命名与下划线自动转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- log日志 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <typeAliases>
        <!--
            别名
            type	类的完全限定名
            alias	别名
        -->
        <!--为某个类指定别名-->
        <!--<typeAlias alias="person" type="com.lihaozhe.pojo.Person"/>-->
        <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
        <package name="com.lihaozhe.pojo"/>
    </typeAliases>
    <!--
        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>
    <mappers>
        <!-- 使用相对于类路径的资源引用 -->
        <!--单独加载某个映射配置文件-->
        <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
        <!-- 使用完全限定资源定位符(URL) -->
        <!--<mapper url="file:///home/lhz/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--<mapper url="file:///D:/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--加载某包下所有的映射配置文件-->
        <package name="com.lihaozhe.mapper"/>
    </mappers>

</configuration>

insert

编写接口

com.lihaozhe.mapper.DeptMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Dept;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/6 上午8:52
 */
public interface DeptMapper {
    
    /**
     * 新增部门
     *
     * @param dept
     * @return
     */
    int insert(@Param("dept") Dept dept);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.DeptMapper">
    <!--新增部门-->
    <insert id="insert">
        INSERT INTO `dept` (`dname`)
        VALUES (#{dept.dname})
    </insert>
</mapper>

测试类

com.lihaozhe.mapper.DeptMapperTest

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

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Dept;
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 2022/7/6 上午8:52
 */
public class DeptMapperTest {
    
    private SqlSession sqlSession;

    // JUnit5 @BeforeAll和@BeforeEach注解替换了 JUnit4 中的@Before注解。
    // 它⽤于表⽰应在当前类中的每个@Test⽅法之前执⾏注解⽅法。
    // 注意:@BeforeAll注解的⽅法必须为静态⽅法,否则它将引发运⾏时错误。
    // 注意:@BeforeEach注解的⽅法不得为静态⽅法,否则它将引发运⾏时错误。
    @BeforeEach
    public void openSqlSession() 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 insert(){
    
        // 获取代理对象
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        // 准备数据
        Dept dept = new Dept("人事部");
        // 插入之前 输出准备数据
        System.out.println("插入之前 dept >>> " + dept);
        // 调用方法执行 insert 操作
        mapper.insert(dept);
        // 手动提交事务
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
        // 插入之后 输出准备数据
        System.out.println("插入之后 dept >>> " + dept);
    }
}

解析结果:

插入之前 dept >>> Dept(did=0, dname=人事部)
Opening JDBC Connection
Created connection 671596011.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2807bdeb]
==>  Preparing: INSERT INTO `dept` (`dname`) VALUES (?)
==> Parameters: 人事部(String)
<==    Updates: 1
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2807bdeb]
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2807bdeb]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2807bdeb]
Returned connection 671596011 to pool.
插入之后 dept >>> Dept(did=0, dname=人事部)

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lihaozhe.mapper.DeptMapper">
    <!--新增部门-->
    <!--
        useGeneratedKeys参数默认是false,
        在进行insert操作后是获取不到自增的id的,
        如果我们需要用到主键需要将参数值改为true
        keyProperty javabean 中主键 filed
        keyColumn datatable 中主键 filed
    -->
    <insert id="insert" parameterType="dept" useGeneratedKeys="true" keyProperty="did" keyColumn="did">
        INSERT INTO `dept` (`dname`)
        VALUES (#{dept.dname})
    </insert>
</mapper>

解析结果:

插入之前 dept >>> Dept(did=0, dname=摸鱼部)
Opening JDBC Connection
Created connection 1643141512.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@61f05988]
==>  Preparing: INSERT INTO `dept` (`dname`) VALUES (?)
==> Parameters: 摸鱼部(String)
<==    Updates: 1
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@61f05988]
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@61f05988]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@61f05988]
Returned connection 1643141512 to pool.
插入之后 dept >>> Dept(did=8, dname=摸鱼部)

update

编写接口

com.lihaozhe.mapper.DeptMappe

package com.lihaozhe.mapper;

import cn.hutool.core.collection.LineIter;
import com.lihaozhe.pojo.Dept;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/6 上午8:52
 */
public interface DeptMapper {
    

    /**
     * 修改部门信息
     *
     * @param dept
     * @return
     */
    int update(@Param("dept") Dept dept);

}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.DeptMapper">
    <!--修改部门信息-->
    <update id="update">
        UPDATE `dept`
        SET `dname` = #{dept.dname}
        WHERE `did` = #{dept.did}
    </update>
</mapper>

测试类

com.lihaozhe.mapper.DeptMappeTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Dept;
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 2022/7/6 上午8:52
 */
public class DeptMapperTest {
    
    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 update() {
    
        // 获取代理对象
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        // 准备数据
        Dept dept = new Dept(8, "企宣部");
        // 调用方法执行 update 操作
        mapper.update(dept);
        // 手动提交事务
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }
}

delete

编写接口

com.lihaozhe.mapper.DeptMappe

package com.lihaozhe.mapper;

import cn.hutool.core.collection.LineIter;
import com.lihaozhe.pojo.Dept;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/6 上午11:13
 */
public interface DeptMapper {
    

    /**
     * 根据部门id删除部门
     *
     * @param did
     * @return
     */
    int deleteByDid(@Param("did") long did);

}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.DeptMapper">
   <!--根据部门id删除部门-->
   <delete id="deleteByDid">
        DELETE
        FROM `dept`
        WHERE `did` = #{did}
    </delete>
</mapper>

测试类

com.lihaozhe.mapper.DeptMappeTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Dept;
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 2022/7/7 下午3:56
 */
public class DeptMapperTest {
    
    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 deleteByDid(){
    
        // 获取代理对象
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        // 调用方法执行 delete 操作
        mapper.deleteByDid(8);
        // 手动提交事务
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }
}

工具类

com.lihaozhe.util.mybatis.MyBatisUtil

package com.lihaozhe.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 2022/7/13 上午11:39
 */
public abstract class MyBatisUtil {
    
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
    private static SqlSessionFactory sqlSessionFactory;
    private static SqlSession sqlSession;

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

    }

    /**
     * 获取 SqlSession 对象
     *
     * @return SqlSession 对象
     */
    public static SqlSession openSqlSession() {
    
        if (threadLocal.get() == null) {
    
            // 从SqlSessionFactory对象中获取SqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 与线程绑定
            threadLocal.set(sqlSession);
            // 返回 SqlSession 对象
            return sqlSession;
        } else {
    
            return threadLocal.get();
        }
    }

    /**
     * 获取 SqlSession 对象
     *
     * @param executorType SIMPLE,REUSE,BATCH
     * @return SqlSession 对象
     */
    public static SqlSession openSqlSession(ExecutorType executorType) {
    
        if (threadLocal.get() == null) {
    
            // 从SqlSessionFactory对象中获取SqlSession
            sqlSession = sqlSessionFactory.openSession(executorType);
            // 与线程绑定
            threadLocal.set(sqlSession);
            // 返回 SqlSession 对象
            return sqlSession;
        } else {
    
            return threadLocal.get();
        }
    }

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

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

    /**
     * 事务回滚 释放资源
     */
    public static void rollbackAndClose() {
    
        if (threadLocal.get() != null) {
    
            sqlSession = threadLocal.get();
            threadLocal.remove();
            sqlSession.rollback();
            sqlSession.close();
        }
    }
}

动态SQL

课工场 layui

@Test
public void test() {
    
    // 需求:查询某部门的(did)  名字有某个字的(ename) 员工列表
    // 分析:
    // 如果不给部门编号则在所有部门中查找 did  不能为 0
    // 如果有部门编号则在某部门内查找
    long did = 3;
    StringBuilder sql = new StringBuilder("select * from person where ");
    if (did != 0) {
    
        sql.append(" did = ?");
        sql.append(" and ename like ");
        sql.append(" '%");
        sql.append(" ? ");
        sql.append("%' ");
    }else {
    
        sql.append(" ename like ");
        sql.append(" '%");
        sql.append(" ? ");
        sql.append("%' ");
    }
    System.out.println(sql.toString());
    // select * from person where  ename like  %  ?  % 
    // select * from person where  did = ? and ename like  %  ?  % 
}

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

sql片段

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

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

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

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.PersonMapper">
    <!--定义SQL片段-->
    <sql id="select_person">
        SELECT *
        FROM `person`
    </sql>
    <!--根据用户ID查询该用户信息 使用SQL片段-->
    <select id="selectById" resultType="person">
        <include refid="select_person"></include>
        WHERE `id` = #{id}
    </select>
</mapper>
编写测试类

com.lihaozhe.mapper.PersonMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.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 openSqlSession() 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 selectById() {
    
        // 获取该接口的代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 执行接口中的方法
        Person person = mapper.selectByid(3);
        // 释放SqlSession资源
        sqlSession.close();
        // 输出查询结果
        System.out.println(person);
    }
}

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

SQL片段

bind

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    

    /**
     * 模糊查找某姓名中包含某个字的用户
     *
     * @param nickname
     * @return
     */
    List<Person> selectByNickname(@Param("nickname") String nickname);
}

编写映射配置文件

src/main/resources/com/lihaozhe/mapper/PersonMapper.xml

<!--定义SQL片段-->
<sql id="select_person">
    SELECT *
    FROM `person`
</sql>

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

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

编写测试类

com.lihaozhe.mapper.PersonMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.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);
        // 准备数据集
        String nickname = "孙";
        // %孙%
        StringBuilder param = new StringBuilder();
        param.append("%");
        if (!StringUtils.isEmpty(nickname)) {
    
            param.append(nickname);
        }
        param.append("%");
        // 执行代理对象中的方法
        List<Person> personList = mapper.selectByNickname(param.toString());
        // 释放资源
        sqlSession.close();
        // 输出结果
        personList.forEach(System.out::println);
    }
}

package com.lihaozhe.mapper;

import com.lihaozhe.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

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    
    
    /**
     * 模糊查找某姓名中包含某个字的男性或女性用户
     *
     * @param nickname
     * @return
     */
    List<Person> selectByGenderAndNickname(@Param("gender") Integer gender, @Param("nickname") String nickname);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.PersonMapper">
    <!--定义SQL片段-->
    <sql id="select_person">
        SELECT *
        FROM `person`
    </sql>
    <!--模糊查找某姓名中包含某个字的男性或女性用户-->
    <select id="selectByGenderAndNickname" resultType="person">
        <include refid="select_person"></include>
        WHERE
        <if test="gender != null">
            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>
    </select>
</mapper>

结果解析:

-- 当方法中参数nickname的值为null或者字符串长度为零
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `gender` = ?
-- 当方法中参数nickname的值不为null且字符串长度大于零
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `gender` = ? AND `nickname` LIKE ?
编写测试类

com.lihaozhe.mapper.PersonMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.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 oepnSqlSession() 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 selectByGenderAndNickname() {
    
        // 获取接口代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 执行代理对象中的方法
        // List<Person> personList = mapper.selectByGenderAndNickname(0,null);
           List<Person> personList = mapper.selectByGenderAndNickname(1,"孙");
        // 释放资源
        sqlSession.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="com.lihaozhe.mapper.PersonMapper">
    <!--定义SQL片段-->
    <sql id="select_person">
        SELECT *
        FROM `person`
    </sql>
    <!--模糊查找某姓名中包含某个字的男性或女性用户-->
    <select id="selectByGenderAndNickname" resultType="person">
        <include refid="select_person"></include>
        WHERE
        <if test="gender != null">
            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>
    </select>
</mapper>

choose (when, otherwise)

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

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

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    

    /**
     * `id`, `uuid`, `mobile` 多个查询条件选择其中一个
     * @param person
     * @return
     */
    Person selectByIdOrUuidOrMobile(@Param("person") Person person);
}

编写映射配置文件

src/main/resources/com/lihaozhe/mapper/PersonMapper.xml

<!--定义SQL片段-->
<sql id="select_person">
    SELECT *
    FROM `person`
</sql>
<!--`id`, `uuid`, `mobile` 多个查询条件选择其中一个-->
<select id="selectByIdOrUuidOrMobile" resultType="person">
    <include refid="select_person"></include>
    <where>
        <choose>
            <when test="person.id != null and person.id > 0">
                `id` = #{person.id}
            </when>
            <when test="person.uuid != null and person.uuid.length > 0">
                `uuid` = #{person.uuid}
            </when>
            <otherwise>
                `mobile` = #{person.mobile}
            </otherwise>
        </choose>
    </where>
</select>

解析结果:

-- 只传入mobile
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `mobile` = ?
-- 传入uuid
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `uuid` = ?
-- 传入id
SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
编写测试类

com.lihaozhe.mapper.PersonMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.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 openSqlSession() 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 selectByIdOrUuidOrMobile() {
    
        // 获取该接口的代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 准备数据
        Person person = new Person();
        // SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `mobile` = ?
        person.setMobile("13352587230");

        // SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `uuid` = ?
		person.setUuid("2032a25e5245454894e192d8d5b17147");

        // SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `id` = ?
        person.setId(33);

        // 执行接口中的方法
        person = mapper.selectByIdOrUuidOrMobile(person);
        // SELECT `id`, `uuid`, `mobile`, `nickname`, `id_card` FROM `person` WHERE `nickname` LIKE ?
        // 释放SqlSession资源
        sqlSession.close();
        // 输出查询结果
        System.out.println(person);
    }
}

trim

编写接口

com.lihaozhe.mapper.EmpMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Emp;
import org.apache.ibatis.annotations.Param;

import java.util.List;

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

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.EmpMapper">
    <!--
        动态SQL if 按照部门ID和姓名中部分关键字查找
        如果没有传递部门ID则在所有部门中查找
        如果传递部门ID但没有传递姓名中的关键字则查找该部门下所有员工列表
        如果部门ID和姓名中的关键字都没有传递则查找所有员工列表
    -->
    <select id="selectByDidAndEname" resultType="emp">
        SELECT * FROM `emp`
        <trim prefix="where" prefixOverrides="AND|OR">
            <if test="emp.did != null and emp.did &gt; 0">
                `did` = #{emp.did}
            </if>
            <if test="emp.ename != null and emp.ename.length &gt; 0">
                <bind name="like_ename" value="'%' + emp.ename + '%'"/>
                AND `ename` like #{like_ename}
            </if>
        </trim>
    </select>
</mapper>

解析 结果:

-- ename 孙
SELECT `eid`, `ename`, `did` FROM `emp` WHERE `ename` Like ?

-- ename ""
SELECT `eid`, `ename`, `did` FROM `emp`

-- did 3
SELECT `eid`, `ename`, `did` FROM `emp` WHERE `did` = ?

-- did 3 并且 ename 孙
SELECT `eid`, `ename`, `did` FROM `emp` WHERE `did` = ? AND `ename` Like ?
编写测试类

com.lihaozhe.mapper.EmpMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Emp;
import com.lihaozhe.util.mybatis.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/8 下午12:02
 */
public class EmpMapperTest {
    
    @Test
    public void selcetByDidAndEname(){
    
        // 使用工具类获取 SqlSession 对象
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        // 获取接口代理对象
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        // 准备数据
        Emp emp = new Emp();
        // SELECT `eid`, `ename`, `did` FROM `emp` WHERE `ename` Like ?
        emp.setEname("孙");

        // SELECT `eid`, `ename`, `did` FROM `emp`
        // emp.setEname("");

        // SELECT `eid`, `ename`, `did` FROM `emp` WHERE `did` = ?
        emp.setDid(3);
        // SELECT `eid`, `ename`, `did` FROM `emp` WHERE `did` = ? AND `ename` Like ?
        // 执行 接口 中的方法
        List<Emp> emps = mapper.selcetByDidAndEname(emp);
        // 使用工具类 释放资源
        MyBatisUtil.close();
        // 输出结果
        emps.forEach(System.out::println);
    }
}

set

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

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

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    
    
    /**
     * 修改员工表数据
     *
     * @param person
     * @return
     */
    int updateById(@Param("person") Person person);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.PersonMapper">
    <!--修改员工表数据-->
    <update id="updateById" parameterType="person">
        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}
    </update>

</mapper>

或者:

<!--修改员工信息-->
<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}
</update>
编写测试类

com.lihaozhe.mapper.PersonMapperTest

package com.lihaozhe.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 com.lihaozhe.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.ArrayList;
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 openSqlSession() 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 updateById() {
    
        // 获取该接口的代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 准备数据
        Person person = new Person();
        String mobile = ChineseMobileNumberGenerator.getInstance().generate();
        System.out.println(mobile);
        // UPDATE `person` SET `mobile` = ? WHERE `id` = ?
        person.setMobile(mobile);
        person.setId(3);

        // 执行接口中的方法
        mapper.updateById(person);
        // 事务提交
        sqlSession.commit();
        // 释放SqlSession资源
        sqlSession.close();
        // 输出结果
        if (status > 0) {
    
            System.out.println("");
        } else {
    
            System.out.println("o(╯□╰)o");
        }
    }

}

foreach

select
编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    

    /**
     * 查找多个id的用户列表
     *
     * @param ids
     * @return
     */
    List<Person> selectByIds(@Param("ids") Long... ids);

}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.PersonMapper">
	 <!--定义SQL片段-->
    <sql id="select_person">
        SELECT *
        FROM `person`
    </sql>
    <!--查找多个id的用户列表-->
    <select id="selectByIds" resultType="person">
        <include refid="select_person"></include>
        <where>
            `id` IN
            <foreach collection="ids" index="index" item="id" open="(" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>
</mapper>
编写测试类

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.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 com.lihaozhe.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.ArrayList;
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 openSqlSession() 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 selectByIds() {
    
        // 获取该接口的代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 执行接口中的方法
        List<Person> personList = mapper.selectByIds(1L, 3L, 5L);
        // 释放SqlSession资源
        sqlSession.close();
        // 输出查询结果
        personList.forEach(System.out::println);
    }
}

delete
编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    
  
    /**
     * 根据传入的id删除用户
     *
     * @param ids
     * @return
     */
    int delByIds(@Param("ids") Long... ids);

}

编写映射配置文件

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

    <!--根据传入的id删除用户-->
    <delete id="delByIds">
        DELETE FROM `person`
        <where>
            `id` IN
            <foreach collection="ids" index="index" item="id" open="(" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </delete>
</mapper>
编写测试类

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.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 com.lihaozhe.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.ArrayList;
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 openSqlSession() 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 delByIds() {
    
        // 获取该接口的代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 执行接口中的方法
        mapper.delByIds(1L, 3L, 5L);
        // 事务提交
        sqlSession.commit();
        // 释放SqlSession资源
        sqlSession.close();
        // 输出结果
        if (status > 0) {
    
            System.out.println("");
            System.out.println(status + " 条记录被删除");
        } else {
    
            System.out.println("o(╯□╰)o");
        }
    }
}

insert

清空person表

truncate person;
编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    

    /**
     * 新增用户列列表
     *
     * @param personList
     * @return
     */
    int insertAll(@Param("personList") List<Person> personList);
}

编写映射配置文件

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

    <!--新增用户列列表-->
    <insert id="insertAll">
        INSERT INTO `person` (`uuid`,`mobile`,`nickname`,`id_card`) VALUES
        <foreach collection="personList" index="index" item="person" separator=",">
            (#{person.uuid},#{person.mobile},#{person.nickname},#{person.idCard})
        </foreach>
    </insert>
</mapper>
编写测试类

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.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 com.lihaozhe.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.ArrayList;
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 openSqlSession() 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 insertAll() {
    
        // 准备数据
        ArrayList<Person> personList = new ArrayList<>();
        for (int i = 0; i < 200; i++) {
    
            Person person = new Person();
            person.setUuid(IdUtil.fastSimpleUUID());
            person.setMobile(ChineseMobileNumberGenerator.getInstance().generate());
            person.setNickname(ChineseNameGenerator.getInstance().generate());
            person.setIdCard(ChineseIDCardNumberGenerator.getInstance().generate());
            System.out.println(person);
            personList.add(person);
        }
        // 获取接口代理对象
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 执行代理对象中的方法
        int status = mapper.insertAll(personList);
        // 事务提交
        sqlSession.commit();
        // 释放资源
        sqlSession.close();

        // 输出结果
        if (status > 0) {
    
            System.out.println("");
            System.out.println("新增了 " + status + " 条记录");
        } else {
    
            System.out.println("o(╯□╰)o");
        }
    }
}

性能
@Test
public void testConetion() 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(ExecutorType.BATCH);
    System.out.println(sqlSession.getConnection());
    // 归还连接给数据源
    sqlSession.close();
}
工具类修改
package com.lihaozhe.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 2022/7/13 上午11:39
 */
public abstract class MyBatisUtil {
    
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
    private static SqlSessionFactory sqlSessionFactory;
    private static SqlSession sqlSession;

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

    }

    /**
     * 获取 SqlSession 对象
     *
     * @return SqlSession 对象
     */
    public static SqlSession openSqlSession() {
    
        if (threadLocal.get() == null) {
    
            // 从SqlSessionFactory对象中获取SqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 与线程绑定
            threadLocal.set(sqlSession);
            // 返回 SqlSession 对象
            return sqlSession;
        } else {
    
            return threadLocal.get();
        }
    }

    /**
     * 获取 SqlSession 对象
     *
     * @param executorType SIMPLE,REUSE,BATCH
     * @return SqlSession 对象
     */
    public static SqlSession openSqlSession(ExecutorType executorType) {
    
        if (threadLocal.get() == null) {
    
            // 从SqlSessionFactory对象中获取SqlSession
            sqlSession = sqlSessionFactory.openSession(executorType);
            // 与线程绑定
            threadLocal.set(sqlSession);
            // 返回 SqlSession 对象
            return sqlSession;
        } else {
    
            return threadLocal.get();
        }
    }

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

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

    /**
     * 事务回滚 释放资源
     */
    public static void rollbackAndClose() {
    
        if (threadLocal.get() != null) {
    
            sqlSession = threadLocal.get();
            threadLocal.remove();
            sqlSession.rollback();
            sqlSession.close();
        }
    }
}

select

一对一

resultType
编写接口

com.lihaozhe.mapper.LoginMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午3:27
 */
public interface LoginMapper {
    

    /**
     * 使用账号和密码登录 获取用户信息
     *
     * @param login 登录参数 account password
     * @return
     */
    Person selectPersonByAccountAndPassword01(@Param("login") Login login);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.LoginMapper">
    <!--使用账号和密码登录 获取用户信息-->
    <select id="selectPersonByAccountAndPassword01" resultType="person">
        SELECT p.id, nickname
        FROM login l
                 INNER JOIN person p
        WHERE pid = p.id
          AND account = #{login.account}
          AND auth_text = #{login.authText}
    </select>
</mapper>

编写测试类

com.lihaozhe.mapper.LoginMapper.Test

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
import com.lihaozhe.pojo.Person;
import com.lihaozhe.util.mybatis.MyBatisUtil;
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 2022/7/7 下午3:29
 */
public class LoginMapperTest {
    
    @Test
    public void selectPersonByAccountAndPassword01(){
    
        Login login = new Login("13925135946", "123456");
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        LoginMapper mapper = sqlSession.getMapper(LoginMapper.class);
        Person person = mapper.selectPersonByAccountAndPassword01(login);
        MyBatisUtil.close();
        System.out.println(person);
    }
}

resultMap 同一映射配置文件内
编写接口

com.lihaozhe.mapper.LoginMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午3:27
 */
public interface LoginMapper {
    

    /**
     * 用户登录
     *
     * @param login 登录参数 account password
     * @return
     */
    Person selectByAccountAndPassword02(@Param("login") Login login);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.LoginMapper">
    <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" resultMap="PersonMap">
        SELECT p.id, nickname
        FROM login l
                 INNER JOIN person p
        WHERE pid = p.id
          AND account = #{login.account}
          AND auth_text = #{login.authText}
    </select>
</mapper>

编写测试类

com.lihaozhe.mapper.LoginMapper.Test

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
import com.lihaozhe.pojo.Person;
import com.lihaozhe.util.mybatis.MyBatisUtil;
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 2022/7/7 下午3:29
 */
public class LoginMapperTest {
    
    @Test
    public void selectPersonByAccountAndPassword02(){
    
        Login login = new Login("13925135946", "123456");
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        LoginMapper mapper = sqlSession.getMapper(LoginMapper.class);
        Person person = mapper.selectPersonByAccountAndPassword02(login);
        MyBatisUtil.close();
        System.out.println(person);
    }
}

resultMap 跨射配置文件内
编写接口

com.lihaozhe.mapper.LoginMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午3:27
 */
public interface LoginMapper {
    

    /**
     * 用户登录
     *
     * @param login 登录参数 account password
     * @return
     */
    Person selectByAccountAndPassword03(@Param("login") Login login);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.LoginMapper">
    <!--使用账号和密码登录 获取用户信息-->
    <select id="selectPersonByAccountAndPassword03" resultMap="com.lihaozhe.mapper.PersonMapper.PersonMap">
        SELECT p.id, nickname
        FROM login l
                 INNER JOIN person p
        WHERE pid = p.id
          AND account = #{login.account}
          AND auth_text = #{login.authText}
    </select>
</mapper>

编写测试类

com.lihaozhe.mapper.LoginMapper.Test

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Login;
import com.lihaozhe.pojo.Person;
import com.lihaozhe.util.mybatis.MyBatisUtil;
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 2022/7/7 下午3:29
 */
public class LoginMapperTest {
    
    @Test
    public void selectPersonByAccountAndPassword03(){
    
        Login login = new Login("13925135946", "123456");
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        LoginMapper mapper = sqlSession.getMapper(LoginMapper.class);
        Person person = mapper.selectPersonByAccountAndPassword03(login);
        MyBatisUtil.close();
        System.out.println(person);
    }
}

association
编写VO类

com.lihaozhe.vo.PersonVo

package com.lihaozhe.vo;

import com.lihaozhe.pojo.Login;
import com.lihaozhe.pojo.Person;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 下午2:19
 */
public class PersonVo implements Serializable {
    
    private static final long serialVersionUID = -2348274078216811958L;
    /**
     * 登录信息
     */
    private Login login;
    /**
     * 员工信息
     */
    private Person person;
}

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.vo.PersonVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午3:27
 */
public interface PersonMapper {
    

    /**
     * 根据id获取用户详细信息包括登录信息
     *
     * @param id 用户ID
     * @return
     */
    PersonVo selectPersonVoById(@Param("id") long id);
}

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

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.PersonMapper">
    <resultMap id="PersonVoMap" type="personVo">
        <association property="login">
            <id property="id" column="lid"/>
            <result property="account" column="account"/>
            <result property="authText" column="auth_text"/>
            <result property="pid" column="pid"/>
        </association>
        <association property="person">
            <id property="id" column="pid"/>
            <result property="uuid" column="uuid"/>
            <result property="mobile" column="mobile"/>
            <result property="nickname" column="nickname"/>
            <result property="idCard" column="id_card"/>
        </association>
    </resultMap>
    <!--根据id获取用户详细信息包括登录信息-->
    <select id="selectPersonVoById" resultMap="PersonVoMap">
        SELECT l.id AS lid,
               account,
               auth_text,
               pid,
               uuid,
               mobile,
               nickname,
               id_card
        FROM login l
                 inner join person p
        WHERE pid = p.id
          and pid = #{id}
    </select>
</mapper>

编写测试类

com.lihaozhe.mapper.PersonMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.vo.PersonVo;
import com.lihaozhe.util.mybatis.MyBatisUtil;
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 2022/7/7 下午3:29
 */
public class PersonMapperTest {
    
    private SqlSession sqlSession;

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

多对一

写法与一对一完全一致

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

编写VO类

com.lihaozhe.vo.SonVo

package com.lihaozhe.vo;

import com.lihaozhe.pojo.Father;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/14 上午9:46
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SonVo implements Serializable {
    
    private static final long serialVersionUID = -203988976020008362L;
    /**
     * 儿子ID
     */
    private long sId;
    /**
     * 儿子姓名
     */
    private String sName;
    /**
     * 该儿子父亲ID
     */
    private long fId;
    /**
     * 该儿子父亲信息
     */
    private Father father;
}

  1. 联合查询
编写接口

com.lihaozhe.mapper.SonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.vo.SonVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 下午3:01
 */
public interface SonMapper {
    
    /**
     * 根据儿子ID查询儿子和该儿子父亲的信息
     *
     * @param sid
     * @return
     */
    SonVo selectSonVoBySid(@Param("sid") long sid);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.SonMapper">
    <!--根据儿子ID查询儿子和该儿子父亲的信息-->
    <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="selectSonVoBySid" 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>

编写测试类

com.lihaozhe.mapper.SonMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.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);
    }
}

  1. 子查询
编写接口

com.lihaozhe.mapper.FatherMapper

package com.lihaozhe.mapper;

import com.lihaozhe.vo.FatherVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 上午11:15
 */
public interface FatherMapper {
    
    /**
     * 根据父亲ID获取父亲信息
     *
     * @param fid
     * @return
     */
    Father selectFatherById(@Param("fid") long fid);
}

com.lihaozhe.mapper.SonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.vo.SonVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 上午11:08
 */
public interface SonMapper {
    
    /**
     * 根据儿子ID查询儿子和该儿子父亲的信息
     *
     * @param sid
     * @return
     */
    SonVo selectSonVoById(@Param("sid") long sid);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.FatherMapper">
    <!--根据父亲ID获取父亲信息-->
    <select id="selectFatherById" resultType="father">
        SELECT *
        FROM `father`
        WHERE `f_id` = #{fid}
    </select>
</mapper>

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.SonMapper">
    <!--根据儿子ID查询儿子和该儿子父亲的信息-->
    <resultMap id="SonVoMapper" 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="com.lihaozhe.mapper.FatherMapper.selectFatherById"/>
    </resultMap>
    <select id="selectSonVoById" resultMap="SonVoMapper">
        SELECT *
        FROM `son`
        WHERE `s_id` = #{sid}
    </select>
</mapper>

编写测试类

com.lihaozhe.mapper.FatherMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.FatherVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 上午11:22
 */
public class FatherMapperTest {
    
    @Test
    public void selectFatherById(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        FatherMapper mapper = sqlSession.getMapper(FatherMapper.class);
        Father father = mapper.selectFatherById(1L);
        MyBatisUtil.close();
        System.out.println(father);
    }
}

com.lihaozhe.mapper.SonMapperTest

package com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 上午11:33
 */
public class SonMapperTest {
    
    @Test
    public void selectSonVoById() {
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        SonMapper mapper = sqlSession.getMapper(SonMapper.class);
        SonVo sonVo = mapper.selectSonVoById(3L);
        MyBatisUtil.close();
        System.out.println(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类

com.lihaozhe.vo.FatherVo

package com.lihaozhe.vo;

import com.lihaozhe.pojo.Son;
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 上午10:27
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FatherVo implements Serializable {
    
    private static final long serialVersionUID = -1335512987974717220L;
    /**
     * 父亲ID
     */
    private long fId;
    /**
     * 父亲姓名
     */
    private String fName;
    /**
     * 该父亲的儿子们
     */
    private List<Son> sons;
}

  1. 子查询
编写接口

com.lihaozhe.mapper.SonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Son;
import com.lihaozhe.vo.SonVo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

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

com.lihaozhe.mapper.FatherMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Father;
import com.lihaozhe.vo.FatherVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 上午11:15
 */
public interface FatherMapper {
    
    /**
     * 根据父亲ID获取父亲及其儿子们的信息
     *
     * @param fid
     * @return
     */
    FatherVo selectFatherVoById(@Param("fid") long fid);
}

编写映射配置文件

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

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.FatherMapper">
    <!--根据父亲ID获取父亲及其儿子们的信息-->
    <resultMap id="FatherVoMap" type="fatherVo">
        <id property="fId" column="f_id"/>
        <result property="fName" column="f_name"/>
        <collection property="sons" ofType="son" column="f_id" select="com.lihaozhe.mapper.SonMapper.selectAllByFId"/>
    </resultMap>
    <select id="selectFatherVoById" resultMap="FatherVoMap">
        select *
        from father
        where f_id = #{fid};
    </select>
</mapper>

编写测试类

com.lihaozhe.mapper.SonMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Son;
import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.SonVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 上午11:33
 */
public class SonMapperTest {
    
    @Test
    public void selectAllByFId(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        SonMapper mapper = sqlSession.getMapper(SonMapper.class);
        List<Son> sons = mapper.selectAllByFId(1L);
        MyBatisUtil.close();
        sons.forEach(System.out::println);
    }
}

com.lihaozhe.mapper.FatherMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Father;
import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.FatherVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 上午11:22
 */
public class FatherMapperTest {
    
    @Test
    public void selectFatherVoById(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        FatherMapper mapper = sqlSession.getMapper(FatherMapper.class);
        FatherVo fatherVo = mapper.selectFatherVoById(1L);
        MyBatisUtil.close();
        System.out.println(fatherVo);
    }
}

解析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
  1. 联合查询用例
编写接口

com.lihaozhe.mapper.FatherMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Father;
import com.lihaozhe.vo.FatherVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 下午3:49
 */
public interface FatherMapper {
    
    /**
     * 根据父亲ID获取父亲及其儿子们的信息
     *
     * @param fid
     * @return
     */
    FatherVo selectFatherVoBySid(@Param("fid") long fid);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.FatherMapper">
    <!--根据父亲ID获取父亲及其儿子们的信息-->
    <resultMap id="FatherVoMapper" type="fatherVo">
        <id property="fId" column="fid"/>
        <result property="fName" column="f_name"/>
        <collection property="sons" ofType="son">
            <id property="sId" column="s_id"/>
            <result property="sName" column="s_name"/>
            <result property="fId" column="fid"/>
        </collection>
    </resultMap>
    <select id="selectFatherVoBySid" resultMap="FatherVoMapper">
        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>

编写测试类

com.lihaozhe.mapper.FatherMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Father;
import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.FatherVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/10 下午3:52
 */
public class FatherMapperTest {
    
    @Test
    public void selectFatherVoBySid(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        FatherMapper mapper = sqlSession.getMapper(FatherMapper.class);
        FatherVo fatherVo = mapper.selectFatherVoBySid(1L);
        MyBatisUtil.close();
        System.out.println(fatherVo);
    }

}

多对多

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

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

编写VO类

com.lihaozhe.vo.CategoryVo

package com.lihaozhe.vo;

import com.lihaozhe.pojo.Book;
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 上午11:27
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CategoryVo implements Serializable {
    
    private static final long serialVersionUID = 5565769402064534797L;
    /**
     * 分类ID
     */
    private long cid;
    /**
     * 分类名称
     */
    private String cname;
    /**
     * 某分类的图书列表
     */
    private List<Book> books;
}

编写接口

com.lihaozhe.mapper.CategoryMapper

package com.lihaozhe.mapper;

import com.lihaozhe.vo.CategoryVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/14 上午11:28
 */
public interface CategoryMapper {
    
    /**
     * 根据分类ID获取分类及其分类下的图书列表
     *
     * @param cid
     * @return
     */
    CategoryVo selectCategoryVoByCid(@Param("cid") long cid);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.CategoryMapper">
    <!--根据分类ID获取分类及其分类下的图书列表-->
    <resultMap id="CategoryVoMap" type="categoryVo">
        <id property="cid" column="cid"/>
        <id property="cname" column="cname"/>
        <collection property="books" ofType="book">
            <id property="bid" column="bid"/>
            <result property="bname" column="bname"/>
        </collection>
    </resultMap>
    <select id="selectCategoryVoByCid" resultMap="CategoryVoMap">
        select cid, cname, bid, bname
        from category
                 inner join book_category
                 inner join book
                            on cid = m_cid and bid = m_bid and cid = #{cid}
    </select>
</mapper>
编写测试类

com.lihaozhe.mapper.CategoryMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.CategoryVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/14 上午11:34
 */
public class CategoryMapperTest {
    

    @Test
    public void selectCategoryVoByCid(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        CategoryMapper mapper = sqlSession.getMapper(CategoryMapper.class);
        CategoryVo categoryVo = mapper.selectCategoryVoByCid(3L);
        MyBatisUtil.close();
        System.out.println(categoryVo);
    }
}

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

编写VO类

com.lihaozhe.vo.BookVo

package com.lihaozhe.vo;

import com.lihaozhe.pojo.Category;
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 上午11:36
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookVo implements Serializable {
    
    private static final long serialVersionUID = 6785352134653400094L;
    /**
     * 书的ID
     */
    private long bid;
    /**
     * 书名
     */
    private String bname;
    /**
     * 某图书的分类信息列表
     */
    private List<Category> categories;
}

编写接口

com.lihaozhe.mapper.BookMapper

package com.lihaozhe.mapper;

import com.lihaozhe.vo.BookVo;
import org.apache.ibatis.annotations.Param;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/14 上午11:38
 */
public interface BookMapper {
    
    /**
     * 根据图书ID查询图书信息包括该图书的分类信息
     *
     * @param bid
     * @return
     */
    BookVo selectBookVoByBid(@Param("bid") long bid);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.BookMapper">
    <!--根据分类ID获取分类及其分类下的图书列表-->
    <resultMap id="BookVoMap" type="bookVo">
        <id property="bid" column="bid"/>
        <result property="bname" column="bname"/>
        <collection property="categories" ofType="category">
            <id property="cid" column="cid"/>
            <id property="cname" column="cname"/>
        </collection>
    </resultMap>
    <select id="selectBookVoByBid" resultMap="BookVoMap">
        select bid, bname, cid, cname
        from book
                 inner join book_category
                 inner join category
                            on bid = m_bid and cid = m_cid and bid = #{bid}
    </select>
</mapper>
编写测试类
package com.lihaozhe.mapper;

import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.BookVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/14 上午11:34
 */
public class BookMapperTest {
    

    @Test
    public void test(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        BookMapper mapper = sqlSession.getMapper(BookMapper.class);
        BookVo bookVo = mapper.selectBookVoByBid(3L);
        MyBatisUtil.close();
        System.out.println(bookVo);
    }
}

间接多对多
编写中间表映射类

com.lihaozhe.pojo.StudentCourse

package com.lihaozhe.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

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

}

编写接口

com.lihaozhe.mapper.CourseMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Course;
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);

}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.mapper.CourseMapper">

    <!--根据课程ID查找课程-->
    <select id="selectCourseByCid" resultType="course">
        SELECT *
        FROM `course`
        WHERE `c_id` = #{cid}
    </select>
</mapper>
编写测试类

com.lihaozhe.mapper.CourseMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Course;
import com.lihaozhe.util.mybatis.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/11 下午3:06
 */
public class CourseMapperTest {
    
    @Test
    public void selectCourseByCid(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
        Course course = mapper.selectCourseByCid(3L);
        MyBatisUtil.close();
        System.out.println(course);
    }
}

编写接口

com.lihaozhe.mapper.StudentMapper

package com.lihaozhe.mapper;

import com.lihaozhe.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);
}

编写映射配置文件

src/main/resources/com/lihaozhe/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="com.lihaozhe.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="com.lihaozhe.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>
编写测试类

com.lihaozhe.mapper.StudentMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.StudentVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/11 下午3:23
 */
public class StudentMapperTest {
    
    @Test
    public void selectStudentVoBySid() {
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        StudentVo studentVo = mapper.selectStudentVoBySid(3L);
        System.out.println(studentVo);
        System.out.println(studentVo.getSName());
        studentVo.getStudentCourses().forEach(studentCourse -> System.out.println(studentCourse.getCourse()));
    }
}

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
编写接口

com.lihaozhe.mapper.StudentMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Student;
import com.lihaozhe.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/com/lihaozhe/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="com.lihaozhe.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="com.lihaozhe.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>
编写测试类

com.lihaozhe.mapper.StudentMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Student;
import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.StudentVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/11 下午3:23
 */
public class StudentMapperTest {
    
     @Test
    public void selectStudentVoBySid() {
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        StudentVo studentVo = mapper.selectStudentVoBySid(3L);
        System.out.println(studentVo);
        System.out.println(studentVo.getSName());
        studentVo.getStudentCourses().forEach(studentCourse -> System.out.println(studentCourse.getCourse()));
    }

    @Test
    public void selectStudentBySid() {
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectStudentBySid(3L);
        System.out.println(student);
    }
}

编写VO类

com.lihaozhe.vo.CourseVo

package com.lihaozhe.vo;

import com.lihaozhe.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;
}

编写接口

com.lihaozhe.mapper.CourseMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Course;
import com.lihaozhe.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/com/lihaozhe/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="com.lihaozhe.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="com.lihaozhe.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>
编写测试类

com.lihaozhe.mapper.CourseMapperTest

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Course;
import com.lihaozhe.util.mybatis.MyBatisUtil;
import com.lihaozhe.vo.CourseVo;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/11 下午3:06
 */
public class CourseMapperTest {
    
    @Test
    public void selectCourseByCid(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
        Course course = mapper.selectCourseByCid(3L);
        MyBatisUtil.close();
        System.out.println(course);
    }

    @Test
    public void selectCourseVoByCid(){
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
        CourseVo courseVo = mapper.selectCourseVoByCid(3L);
        System.out.println(courseVo);
        System.out.println(courseVo.getCName());
        courseVo.getStudentCourses().forEach(studentCourse -> System.out.println(studentCourse.getStudent()));
    }
}

延迟加载

@Test
public void selectStudentVoBySid01() {
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    StudentVo studentVo = mapper.selectStudentVoBySid(1L);
    MyBatisUtil.close();
    System.out.println(studentVo.getSName());
}

Opening JDBC Connection
Created connection 379121284.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
==>  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
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
Returned connection 379121284 to pool.
@Test
public void selectStudentVoBySid02() {
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    StudentVo studentVo = mapper.selectStudentVoBySid(1L);
    MyBatisUtil.close();
    studentVo.getStudentCourses().forEach(studentCourse -> System.out.println(studentCourse.getCourse()));
}
Opening JDBC Connection
Created connection 379121284.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
==>  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
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
Returned connection 379121284 to pool.
Course(cId=1, cName=java)
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="emps" ofType="emp" column="did" select="com.lihaozhe.mapper.EmpMapper.selectByDid" fetchType="lazy"/>-->
<!--<collection property="emps" ofType="emp" column="did" select="com.lihaozhe.mapper.EmpMapper.selectByDid" fetchType="eager"/>-->
@Test
public void selectStudentVoBySid01() {
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    StudentVo studentVo = mapper.selectStudentVoBySid(1L);
    MyBatisUtil.close();
    System.out.println(studentVo.getSName());
}

Opening JDBC Connection
Created connection 379121284.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
==>  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
<==        Row: 1, stu01, 2
<==      Total: 2
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1698ee84]
Returned connection 379121284 to pool.
@Test
public void selectStudentVoBySid02() {
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    StudentVo studentVo = mapper.selectStudentVoBySid(1L);
    MyBatisUtil.close();
    studentVo.getStudentCourses().forEach(studentCourse -> System.out.println(studentCourse.getCourse()));
}
Opening JDBC Connection
Created connection 544966217.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
==>  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
<==        Row: 1, stu01, 2
<==      Total: 2
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
Returned connection 544966217 to pool.
Opening JDBC Connection
Checked out connection 544966217 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
==>  Preparing: SELECT * FROM `course` WHERE `c_id` = ?
==> Parameters: 1(Integer)
<==    Columns: c_id, c_name
<==        Row: 1, java
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
Returned connection 544966217 to pool.
Course(cId=1, cName=java)
Opening JDBC Connection
Checked out connection 544966217 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
==>  Preparing: SELECT * FROM `course` WHERE `c_id` = ?
==> Parameters: 2(Integer)
<==    Columns: c_id, c_name
<==        Row: 2, scala
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
Returned connection 544966217 to pool.
Course(cId=2, cName=scala)

Process finished with exit code 0

侵入式延迟 积极加载

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

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

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

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

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

  • fetchType=“lzsy” 支持懒加载

@Test
public void selectDeptVoById01() {
    
    // 获取接口代理对象
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    // 执行代理对象中方法
    mapper.selectDeptVoById(3L);
    // 释放资源
    sqlSession.close();
}
@Test
public void selectDeptVoById02() {
    
    // 获取接口代理对象
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    // 执行代理对象中方法
    DeptVo deptVo = mapper.selectDeptVoById(3L);
    // 释放资源
    sqlSession.close();
    System.out.println(deptVo.getDname());
}
@Test
public void selectDeptVoById03() {
    
    // 获取接口代理对象
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    // 执行代理对象中方法
    DeptVo deptVo = mapper.selectDeptVoById(3L);
    // 释放资源
    sqlSession.close();
    deptVo.getEmps();
}

一级缓存

编写测试类

com.lihaozhe.mapper.CacheTest

package com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/12 上午10:05
 */
public class CacheTest {
    
    @Test
    public void testCache01(){
    
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        Person person01 = mapper.selectById(3L);
        Person person02 = mapper.selectById(3L);
        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 com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/12 上午10:05
 */
public class CacheTest {
    
    @Test
    public void testCache02() {
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        Person person01 = mapper.selectById(6L);
        Person person02 = mapper.selectById(3L);
        MyBatisUtil.close();
    }0
}

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 com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/12 上午10:05
 */
public class CacheTest {
    

    @Test
    public void testCache03() {
    
        SqlSession sqlSession = MyBatisUtil.openSqlSession();
        PersonMapper mapper01 = sqlSession.getMapper(PersonMapper.class);
        PersonMapper mapper02 = sqlSession.getMapper(PersonMapper.class);
        Person person01 = mapper01.selectById(3L);
        Person person02 = mapper02.selectById(3L);
        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 com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/12 上午10:05
 */
public class CacheTest {

    @Test
    public void testCache04() {
        SqlSession sqlSession01 = MyBatisUtil.getSqlSession();
        PersonMapper mapper01 = sqlSession01.getMapper(PersonMapper.class);
        Person person01 = mapper01.selectById(3L);

        SqlSession sqlSession02 = MyBatisUtil.getSqlSession();
        PersonMapper mapper02 = sqlSession02.getMapper(PersonMapper.class);
        Person person02 = mapper02.selectById(3L);
        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 com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/12 上午10:05
 */
public class CacheTest {
    

    @Test
    public void testCache05() {
    
        SqlSession sqlSession01 = MyBatisUtil.getSqlSession();
        PersonMapper mapper01 = sqlSession01.getMapper(PersonMapper.class);
        Person person01 = mapper01.selectById(3L);
        MyBatisUtil.close();
        
        SqlSession sqlSession02 = MyBatisUtil.getSqlSession();
        PersonMapper mapper02 = sqlSession02.getMapper(PersonMapper.class);
        Person person02 = mapper02.selectById(3L);
        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.9.9</version>
</dependency>
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.2</version>
</dependency>

编写核心配置文件

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

编写映射配置文件

src/main/resources/com/lihaozhe/mapper/PersonMapper.xml

添加以下内容

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

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

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

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

<select id="selectById" resultType="person" useCache="true">
    <include refid="select_person"></include>
    <where>
        `id` = #{id}
    </where>
</select>

编写测试类

package com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/12 上午10:05
 */
public class CacheTest {
    

    @Test
    public void testCache05() {
    
        SqlSession sqlSession01 = MyBatisUtil.openSqlSession();
        PersonMapper mapper01 = sqlSession01.getMapper(PersonMapper.class);
        Person person01 = mapper01.selectById(3L);
        MyBatisUtil.close();

        SqlSession sqlSession02 = MyBatisUtil.openSqlSession();
        PersonMapper mapper02 = sqlSession02.getMapper(PersonMapper.class);
        Person person02 = mapper02.selectById(3L);
        MyBatisUtil.close();
    }
}

Cache Hit Ratio [com.lihaozhe.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Created connection 1045731788.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e5499cc]
==>  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@3e5499cc]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e5499cc]
Returned connection 1045731788 to pool.
Cache Hit Ratio [com.lihaozhe.mapper.PersonMapper]: 0.5
package com.lihaozhe.mapper;

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

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/12 上午10:05
 */
public class CacheTest {
    

    @Test
    public void testCache05() {
    
        SqlSession sqlSession01 = MyBatisUtil.openSqlSession();
        PersonMapper mapper01 = sqlSession01.getMapper(PersonMapper.class);
        Person person01 = mapper01.selectById(3L);
        MyBatisUtil.close();

        SqlSession sqlSession02 = MyBatisUtil.openSqlSession();
        PersonMapper mapper02 = sqlSession02.getMapper(PersonMapper.class);
        Person person02 = mapper02.selectById(3L);
        MyBatisUtil.close();
    }
}

Cache Hit Ratio [com.lihaozhe.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Created connection 1045731788.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e5499cc]
==>  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
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e5499cc]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e5499cc]
Returned connection 1045731788 to pool.
Cache Hit Ratio [com.lihaozhe.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Checked out connection 1045731788 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e5499cc]
==>  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@3e5499cc]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3e5499cc]
Returned connection 1045731788 to pool.

分页插件 pagehelper

引入maven依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.1</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>com.lihaozhe</groupId>
  <artifactId>mybatis</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>mybatis</name>
  <packaging>jar</packaging>

  <properties>
    <jdk.version>17</jdk.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <maven.compiler.compilerVersion>17</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.8.2</junit.version>

    <fastjson.version>2.0.6</fastjson.version>
    <gson.version>2.9.0</gson.version>
    <hutool.version>5.8.0.M3</hutool.version>
    <jackson.version>2.13.3</jackson.version>
    <lombok.version>1.18.24</lombok.version>
    <java-testdata.version>1.1.2</java-testdata.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.github.binarywang</groupId>
      <artifactId>java-testdata-generator</artifactId>
      <version>${java-testdata.version}</version>
    </dependency>
    <!--commons-->
    <!--commons-lang3-->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.8.1</version>
    </dependency>
    <!--commons-io-->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.11.0</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>${hutool.version}</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>
    <!--json-->
    <!--fastjson-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson.version}</version>
    </dependency>
    <!--gson-->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>${gson.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>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.29</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.10</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.8</version>
    </dependency>
    <dependency>
      <groupId>org.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>3.9.9</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-ehcache</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.3.1</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.20</version>
    </dependency>
  </dependencies>

  <build>
    <!--项目打包文件名-->
    <!--<finalName>news</finalName>-->
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.3.2</version>
      </plugin>
      <!-- 编译级别 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</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.2.0</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
      <!-- 打包的时候跳过测试junit begin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</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="com.lihaozhe.pojo.Person"/>-->
        <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
        <package name="com.lihaozhe.pojo"/>
        <package name="com.lihaozhe.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>
    <mappers>
        <!-- 使用相对于类路径的资源引用 -->
        <!--单独加载某个映射配置文件-->
        <!--<mapper resource="mybatis/mapper/PersonMapper.xml"/>-->
        <!-- 使用完全限定资源定位符(URL) -->
        <!--<mapper url="file:///home/lhz/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--<mapper url="file:///D:/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!--加载某包下所有的映射配置文件-->
        <package name="com.lihaozhe.mapper"/>
    </mappers>

</configuration>

编写接口

com.lihaozhe.mapper.PersonMapper

package com.lihaozhe.mapper;

import com.lihaozhe.pojo.Person;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 李昊哲
 * @version 1.0.0 2022/7/7 下午1:47
 */
public interface PersonMapper {
    

    /**
     * 分页查询
     * @param person
     * @return
     */
    List<Person> select4page(@Param("person") Person person);
}

编写映射的配置文件

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

    <select id="select4page" resultType="person">
        SELECT *
        FROM `person`
        <trim prefix="where" prefixOverrides="AND|OR">
            <if test="person.mobile != null and person.mobile.length &gt; 0 and person.mobile.length &lt; 12">
                <bind name="like_mobile" value="'%'+ person.mobile +'%'"/>
                AND `mobile` LIKE #{like_mobile}
            </if>
            <if test="person.mobile != null and person.mobile.length &gt; 0 and person.mobile.length &lt; 12">
                <bind name="like_nickname" value="'%'+ person.nickname +'%'"/>
                AND `nickname` LIKE #{like_nickname}
            </if>
        </trim>
    </select>
</mapper>

编写测试类

com.lihaozhe.mapper.PersonMapperTest

package com.lihaozhe.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 com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lihaozhe.pojo.Person;
import com.lihaozhe.util.mybatis.MyBatisUtil;
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.ArrayList;
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 select4page(){
    
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        // 在执行代理对象中查询方法之前 开启分页
        PageHelper.startPage(10,5);
        // 准备查询条件
        Person person = new Person();
        // 开启分页后 执行代理对象中的查询方法
        List<Person> personList = mapper.select4page(person);
        sqlSession.close();
        PageInfo<Person> pageInfo = new PageInfo<>(personList);
        System.out.println(pageInfo);
        System.out.println(JSONObject.toJSONString(pageInfo));
        System.out.println(JSONObject.toJSONString(pageInfo.getList()));
    }
}

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="login" type="com.lihaozhe.pojo.Login"/>-->
        <!--为某个包下的所有类指定别名 别名默认为类的首字母小写之后的字符串-->
        <package name="com.lihaozhe.pojo"/>
        <package name="com.lihaozhe.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="com/lihaozhe/mapper/PersonMapper.xml"/>-->
        <!-- 使用完全限定资源定位符(URL) -->
        <!--<mapper url="file:///home/lhz/ideaproject/mybatis/src/main/resources/com/lihaozhe/mapper/PersonMapper.xml" />-->
        <!-- 将包内的映射器接口实现全部注册为映射器 -->
        <package name="com.lihaozhe.mapper"/>
    </mappers>
</configuration>
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_24330181/article/details/125812943

智能推荐

info级别日志与debug_debug中的计算是否在info级别也会跑-程序员宅基地

文章浏览阅读6.3k次。日志默认info级别debug日志不会打印,但是会执行日志填充的数据例如:logger.debug("日志输出",2*10); 1. 2*10会先执行出结果,然后继续往下走2. 在ch.qos.logback.classic.Logger#filterAndLog_1方法中判断是否符合级别要求是否需要输出3.如图:..._debug中的计算是否在info级别也会跑

Third calibration example - Calibration using Heikkil�'s data (planar and non-planar calibration rig-程序员宅基地

文章浏览阅读1.4k次。Similarly to the previous example, let us apply our calibration engine onto the data that comes with the originalcalibration toolbox of Heikkil� from the University of Oulu. Once again. do not bothe_non-planar calibration

物联网常用的网络协议:MQTT、AMQP、HTTP、CoAP、LwM2M_lmm2m和mqtt-程序员宅基地

文章浏览阅读1w次,点赞10次,收藏63次。物联网常用的网络协议:MQTT、AMQP、HTTP、CoAP、LwM2M物联网设备间沟通的语言,就是网络协议。设备间想相互交流,通信双方必须使用同一种“语言”。比如说你和中国人问好说’你好‘、日本人问好要说‘こんにちは’、和英国人问好要说‘hello’.说起网络协议,你可能马上就想到了 HTTP 协议。是的,在日常的 Web 开发中,我们总是需要跟它打交道,因为 HTTP 协议是互联网的主流网络协议。类似地,应用在互联网中的网络协议,还有收发电子邮件的 POP3 、SMTP 和 IMAP 协议,以及_lmm2m和mqtt

fortran使用MKL函数库中的geev计算一般矩阵的特征值与特征向量_fortran求矩阵特征值-程序员宅基地

文章浏览阅读7.4k次,点赞4次,收藏20次。这篇博文简要记录一下使用MKL函数库计算一般矩阵的特征值与特征向量对形如对称矩阵或是埃尔米特等特殊矩阵有其对应的子程序,在这里先不涉及。有需求的可以自行查阅MKL官方文档下面给出本次示例代码:代码使用f95接口。f77借口参数太多,笔者太懒<不过懒惰是创新的原动力^_^>program testGeev use lapack95 implicit..._fortran求矩阵特征值

Numpy, Scipy, Matplotlib基本用法_np.imresize-程序员宅基地

文章浏览阅读147次。学习内容来自:Numpy Tutorial文章目录Array SlicingArray IndexingMathematical ManipulationBroadcastingImage Processing基本的用法课程里面说的挺详细了。 特别记录一些需要关注的点。Array Slicing使用固定数字进行array寻址会导致数组降维。y = np.random.random((3,..._np.imresize

蓝桥杯 历届试题 回文数字 C++_c++蓝桥杯 回文数-程序员宅基地

文章浏览阅读355次。题目阅览 观察数字:12321,123321 都有一个共同的特征,无论从左到右读还是从右向左读,都是相同的。这样的数字叫做:回文数字。  本题要求你找到一些5位或6位的十进制数字。满足如下要求:  该数字的各个数位之和等于输入的整数。  输入格式  一个正整数 n (10<n<100), 表示要求满足的数位和。  输出格式若干行,每行包含一个满足要求的5位或6位整数。  数字按从小到大的顺序排列。  如果没有满足条件的,输出:-1样例输入144样例输出199899_c++蓝桥杯 回文数

随便推点

Java生成二维码,扫描并跳转到指定的网站_java扫二维码进入自己制作的网页-程序员宅基地

文章浏览阅读6.2k次,点赞3次,收藏13次。需要的pom文件 &lt;dependency&gt; &lt;groupId&gt;com.google.zxing&lt;/groupId&gt; &lt;artifactId&gt;core&lt;/artifactId&gt; &lt;version&gt;3.1.0&lt;/version&gt;_java扫二维码进入自己制作的网页

python:多波段遥感影像分离成单波段影像_一个多波段影像分解成多个单波段影像-程序员宅基地

文章浏览阅读650次。在遥感图像处理中,我们经常需要将多波段遥感影像拆分成多个单波段图像,以便进行各种分析和后续处理。本篇博客将介绍一个用Python编写的程序,该程序可以读取多波段遥感影像,将其拆分为单波段图像,并保存为单独的文件。本程序使用GDAL库来处理遥感影像数据,以及NumPy库来进行数组操作。结果如下图所示,选中的影像为输入的多波段影像,其他影像分别为拆分后的多波段影像。_一个多波段影像分解成多个单波段影像

移动硬盘突然在电脑上无法显示_电脑无法显示移动硬盘-程序员宅基地

文章浏览阅读5.1k次,点赞2次,收藏4次。0前言一直用的好好的移动硬盘突然不显示了,前段时间因为比较忙,一直没顾得上管它,趁这个假期,好好捅咕了一番,总算是弄好了,特此将解决的过程记录如下:1.问题描述 1.我的移动硬盘在其他人的电脑上能够正常显示和使用 2.其他移动硬盘在我电脑上能够正常的显示和使用 3.在我的电脑上,该移动硬盘,既不显示盘符,磁盘管理 又不显示该磁盘2.问题分析1.我的移动硬盘能够在其他人电脑上_电脑无法显示移动硬盘

Linux开机启动过程(16):start_kernel()->rest_init()启动成功_linux 标志着kernel启动完成-程序员宅基地

文章浏览阅读1k次。Kernel initialization. Part 10.在原文的基础上添加了5.10.13部分的源码解读。End of the linux kernel initialization processThis is tenth part of the chapter about linux kernel initialization process and in the previous part we saw the initialization of the RCU and stopped o_linux 标志着kernel启动完成

Scala安装和开发环境配置教程_scala安装及环境配置-程序员宅基地

文章浏览阅读5.3k次,点赞5次,收藏23次。Scala语言概述:Scala语言是一门以Java虚拟机为运行环境,支持面向对象和函数式编程的静态语言,java语言是面向对象的,所以代码写起来就会相对比较模块儿,而函数式编程语言相对比较简洁_scala安装及环境配置

深扒人脸识别60年技术发展史_人脸识别发展历史-程序员宅基地

文章浏览阅读2.4k次。“他来听我的演唱会,门票换了手铐一对”。最近歌神张学友变阿SIR,演唱会上频频抓到罪犯,将人脸识别技术又一次推到了大众的视线中。要说人脸识别技术的爆发,当属去年9月份苹果iPhone x的发布,不再需要指纹,只需要扫描面部就可以轻松解锁手机。任何技术一旦进入智能手机这个消费市场,尤其是被苹果这个标志性的品牌采用,就意味着它将成为一种趋势,一个智能设备的标配。在智能手机快速崛起的这几年,其密码锁..._人脸识别发展历史