MyBatis基础教程

news2024/11/26 4:44:48

文章目录

  • 一、MyBatis基本使用
    • 1.1简介
    • 1.2搭建MyBatis环境
      • 1.2.1安装MyBatis
      • 1.2.2创建MyBatis核心配置文件
      • 1.2.3创建mapper接口
      • 1.2.4创建MyBatis映射文件
      • 1.2.5实现增加功能
    • 1.3整合log4j
    • 1.4修改与删除功能
    • 1.5查询功能
      • 1.5.1查询单个实体类对象
      • 1.5.2查询所有用户信息
  • 二、核心配置文件详解
    • 2.1environments
    • 2.2properties
    • 2.3typeAliases
    • 2.4mappers
  • 三、MyBatis获取参数的两种方式
    • 3.1封装获取SqlSessionUtils工具类
    • 3.2参数为单个字面量类型
    • 3.3参数为多个字面量类型
    • 3.4Map类型参数传递
    • 3.5实体类类型参数传递
    • 3.6@Param命名参数
      • 3.6.1基本使用
      • 3.6.2源码解析
  • 四、MyBatis各种查询方法
    • 4.1查询一个实体类对象
    • 4.2查询多个实体类对象
    • 4.3查询字面量
    • 4.4查询Map集合
      • 4.4.1将查询出的单行数据转化为Map集合
      • 4.4.2将查询出的多行数据转化为Map集合
  • 五、特殊SQL的执行
    • 5.1模糊查询
    • 5.2批量删除
    • 5.3动态设置表名
    • 5.4获取添加功能自增的主键
  • 六、自定义映射关系
    • 6.1使用字段别名
    • 6.2使用全局配置mapUnderscoreToCamelCase
    • 6.3自定义映射resultMap:一对一映射
    • 6.4自定义映射resultMap:多对一映射
      • 6.4.1级联属性赋值方式
      • 6.4.2association方式
      • 6.4.3分步查询方式
      • 6.4.4延迟加载
    • 6.5一对多映射
      • 6.5.1collection标签
      • 6.5.2分步查询
  • 七、动态SQL
    • 7.1if:单条件分支条件判断
    • 7.2where标签
    • 7.3trim标签
    • 7.4choose、when、otherwise
    • 7.5foreach标签
      • 7.5.1批量删除:Array实现
      • 7.5.2批量添加:List实现
      • 7.5.3批量查询:Map实现
    • 7.6SQL片段
  • 八、缓存
    • 8.1一级缓存机制
    • 8.2二级缓存
    • 8.3二级缓存的相关配置
    • 8.4缓存查询顺序
  • 九、分页插件
    • 9.1配置方法
    • 9.2进行测试


一、MyBatis基本使用

1.1简介

在这里插入图片描述
在这里插入图片描述

  什么是持久化?:持久化是将程序数据在持久状态和瞬时状态间转换的机制,即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等,JDBC就是一种持久化机制、文件IO也是一种持久化机制。

  什么是持久层?:持久层(dao层,Data Access Object数据访问对象)用于完成持久化工作,即,将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。

MyBatis具有以下特性
在这里插入图片描述
与其他持久化层技术对比
在这里插入图片描述

MyBatis官方地址:MyBatis3

1.2搭建MyBatis环境

1.2.1安装MyBatis

  引入依赖:

<!--MyBatis 依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>
<!--junit5测试-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
</dependency>
<!--MySQL驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>

  其中,mysql-connector-java是MySQL提供的JDBC驱动包,用JDBC连接MySQL数据库时必须使用该jar包。注意,本文中安装的MySQL版本为8.0.18。

1.2.2创建MyBatis核心配置文件

  习惯上将MyBatis核心配置文件命名为mybatis-config.xml,在整合Spring之后可省略。核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息,存放在src/main/resources目录下。可在IDEA中保存配置文件的官方模板:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!--配置连接数据库的环境-->
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <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="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

在这里插入图片描述
在之后创建配置文件时只需输入信息即可:
在这里插入图片描述
所输入信息为:

File name:mybatis-config.xml
driver:com.mysql.cj.jdbc.Driver
url:jdbc:mysql://localhost:3306/testdatabase?serverTimezone=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true
username:root
password:123456

其中,数据库testdatabase会在下文中创建。

1.2.3创建mapper接口

  MyBatis中的mapper接口相当于以前的dao接口,但区别在于mapper仅仅是接口,无需创建实现类。当调用接口中的方法时,就会自动找到对应的SQL语句并执行。
1.创建数据库及user表

create database testdatabase;
use testdatabase;
create table table_user
(
    id       int auto_increment,
    name     varchar(20) null,
    password varchar(20) null,
    age      int         null,
    sex      char        null,
    email    varchar(20) null,
    constraint table_user_pk
        primary key (id)
);

2.创建实体类User:本项目中将所有实体类放在同一目录Pojo下。

package org.example.Pojo;

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private String sex;
    private String email;

    public User() {
    }

    public User(Integer id, String username, String password, Integer age, String sex, String email) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
        this.sex = sex;
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

3.创建mapper接口替代dao功能:本项目中将所有的mapper接口放在同一目录mapper下。

package org.example.mapper;
public interface UserMapper {
    //添加用户信息
    int insertUser();
}

1.2.4创建MyBatis映射文件

  相关概念:ORM(Object Relationship Mapping 对象映射文件)

  • 对象:Java的实体类对象。
  • 关系:关系型数据库。
  • 映射:二者之间的对应关系。

在这里插入图片描述
  映射文件的命名规则:表所对应的实体类名+Mapper.xml,如表t_user映射实体类为User,则对应映射文件名为UserMapper.xml。因此,一个映射文件对应一个实体类,对应一张表的操作。创建映射文件(可从官方文档获取模板):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--1.映射文件的namespace要和mapper接口的全类名保持一致,当调用mapper
            接口方法时就会根据mapper接口的全类名自动寻找映射文件-->
<mapper namespace="org.example.mapper.UserMapper">
    <!--2.映射文件中SQL语句的id要和mapper接口中的方法名一致,当调用方法时就会自动匹配对应的SQL语句-->
    <!--int insertUser();-->
    <insert id="insertUser">
        insert into table_user values(null,'admin','123456',23,'男','123456@qq.com')
    </insert>
</mapper>

注意,MyBatis面向接口编程的两个一致:

  • 1.映射文件的namespace要和mapper接口的全类名保持一致。
  • 2.映射文件中SQL语句的id要和mapper接口中的方法名一致。

  创建mapper映射文件后需在mybatis-config.xml中引入该配置文件:

<!--引入映射文件-->
<mappers>
    <mapper resource="mappers/UserMapper.xml"/>
</mappers>

1.2.5实现增加功能

  MyBatis中提供了操作数据库的会话对象SqlSession:

package org.example;

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.example.mapper.UserMapper;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;

public class testMyBatis {
    @Test
    public void UserTest() throws IOException {
        //1.加载核心配置文件
        InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
        //2.获取SqlSessionFactoryBuilder(提供SqlSession工厂对象的构建对象)
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        //3.通过核心配置文件对应的字节输入流创建工厂对象sqlSessionFactory,生产SqlSession对象
        SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(inputStream);
        //4.获取SqlSession对象,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
        //SqlSession sqlSession=sqlSessionFactory.openSession();
        //4.在创建时输入参数true,之后操作的sql都会自动提交
        SqlSession sqlSession=sqlSessionFactory.openSession(true);
        //5.通过代理模式创建UserMapper接口的代理实现类对象
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        //6.调用UserMapper接口中的方法,就可根据UserMapper的全类名匹配元素文件,并通过调用的方法名匹配映射文件中的SQL标签、执行SQL语句
        int result= userMapper.insertUser();//自动完成提交
        System.out.println(result);
    }
}

在这里插入图片描述
在这里插入图片描述

  • SQLSession:代表Java程序和数据库之间的会话。
  • SqLSessionFactory:生产SQLSession的工厂。
  • 工厂模式:若要创建某一对象切使用的过程基本固定,则可将创建该对象的相关代码封装到一个工程类中,之后使用该工厂类来生产所需的对象。

1.3整合log4j

  在src/main/resources下加入log4j配置文件log4j.properties

#声明日志的输出级别及输出方式
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
# MyBatis 日志配置
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
#声明日志的输出位置在控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#定义日志的打印格式  %t 表示线程名称  %5p表示输出日志级别   %n表示换行
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

引入相关依赖:

<!--log4j依赖-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

1.4修改与删除功能

1.修改功能
  在mapper接口中增加update方法:

package org.example.mapper;
public interface UserMapper {
    //修改用户信息
    void updateUser();
}

  修改mapper映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.UserMapper">
    <!--void updateUser();-->
    <update id="updateUser">
        update table_user set name='张三' where id=4
    </update>
</mapper>

  进行测试:

@Test
public void testUpdate() throws IOException {
    InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession=sqlSessionFactory.openSession(true);
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    userMapper.updateUser();
}

在这里插入图片描述
2.删除功能
  mapper接口:

package org.example.mapper;
public interface UserMapper {
    //删除用户信息
    void deleteUser();
}

  mapper映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.UserMapper">
    <!--void deleteUser();-->
    <delete id="deleteUser">
        delete from table_user where id=5;
    </delete>
</mapper>

  进行测试:

@Test
public void testDelete() throws IOException {
    InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession=sqlSessionFactory.openSession(true);
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    userMapper.deleteUser();
}

在这里插入图片描述

1.5查询功能

  使用查询功能时应明确查询结果的类型,可能是实体类对象、实体类对象的集合、单列数据等。

1.5.1查询单个实体类对象

  mapper接口中声明方法:

package org.example.mapper;
import org.example.Pojo.User;
public interface UserMapper {
    //根据id查询用户信息
    User getUserById();
}

  修改mapper映射文件,由于返回值是实体类类型,故需使用resultType进行声明:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.UserMapper">
    <!--User getUserById();-->
    <select id="getUserById" resultType="org.example.Pojo.User">
        select * from table_user where id=3
    </select>
</mapper>
  • resultType:设置默认的映射关系。得到单行的查询结果后,会自动将结果的字段名作为属性名并进行赋值,无法匹配则不赋值(本例中表中name与实体类属性username无法映射自动赋值,故为null)。
  • resultMap:设置自定义的映射关系(后文中有详解)。
      进行测试:
@Test
public void testSelect() throws IOException {
    InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession=sqlSessionFactory.openSession(true);
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    User user=userMapper.getUserById();
    System.out.println(user);
}

在这里插入图片描述

1.5.2查询所有用户信息

  mapper接口中声明方法:

package org.example.mapper;
import org.example.Pojo.User;
import java.util.List;
public interface UserMapper {
    //查询所有用户信息
    List<User> getAllUser();
}

  修改mapper映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.UserMapper">
    <!--List<User> getAllUser();-->
    <select id="getAllUser" resultType="org.example.Pojo.User">
        select * from table_user;
    </select>
</mapper>

  进行测试:

@Test
public void testSelect() throws IOException {
    InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession=sqlSessionFactory.openSession(true);
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    List<User> list=userMapper.getAllUser();
    System.out.println(list);
}

在这里插入图片描述

二、核心配置文件详解

  核心配置文件中的标签必须按固定的顺序:
  propertiessettingstypeAliasestypeHandlersobjectFactoryobjectWrapperFactoryreflectorFactorypluginsenvironmentsdatabaseIdProvidermapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!--配置连接数据库的环境-->
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <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="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

2.1environments

  <environments>是复数标签,故而可配置多个连接数据库的环境,有属性default,用于指定默认使用的环境(使用mybatis创建SqlSessionFactory对象未指定环境时使用的环境,一般一个数据库只会对应一个SqlSessionFactory对象),值为<environment>标签的id值。而<environment>标签用于配置某个具体的环境,其id属性是该环境的唯一标识,不能重复。<environment>标签有以下子标签:

  • <transactionManager>:使用唯一属性type设置事务管理方式,且有如下两种取值:
    • MANAGED:mybatis不再负责事务的管理了。事务交给其他容器来负责,如spring。当前是单纯的mybatis环境,事务不被任何机制负责,则事务功能关闭。
    • JDBC:执行SQL时使用JDBC中原生的事务管理方式,即,默认开启事务,且事务的提交与回滚需要手动处理。可使用以下方式关闭事务:
SqlSession sqlSession = sqlSessionFactory.openSession(true);  //关闭事务,转为自动提交
  • <dataSource>:为程序提供Connection对象,一般为程序提供Connection对象的称为数据源。数据源是一种规范,JDK中提供了javax.sql.DataSource接口作为数据源规范,可通过实现接口来定义数据源。类似的,数据库连接池也是提供连接对象的,故所有数据库连接池都是数据源,如druid、c3p0、dbcp等。<dataSource>标签有唯一属性type,用于指定数据源类型,共有三种取值:
    • UNPOOLED:不使用数据库连接池技术。每一次请求过来之后,都是创建新的Connection对象。
    • POOLED:使用mybatis自己的数据库连接池技术。
    • JNDI:集成第三方的数据库连接池。JNDI是一套规范。大部分 Web 容器,如tomcat、jetty等都实现了 JNDI 规范。
  • <property><dataSource>的子标签,用于配置属性(键值对)。

  注意,当type属性值不同时,可使用的子标签也不同,参考官方文档:

1.UNPOOLED
在这里插入图片描述
2.POOLED
在这里插入图片描述
3.JDNI
在这里插入图片描述
在这里插入图片描述

2.2properties

  <properties>是复数标签,可使用子标签<property>配置属性(键值对,配置方式同<dataSource>下的<property>标签),也可引用外部配置文件进行配置。在同级目录下创建配置文件db.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testdatabase?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
jdbc.username=root
jdbc.password=123456
  • 在核心配置文件中使用属性resource指定同级目录下(类路径下)的配置文件db.properties
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
  • 当不在同一类路径下时,需使用url属性,使用绝对路径来指定文件。事实上,平时使用的文件路径都是基于file协议的,只是Windows文件夹系统的文件夹省略了,使用时需加上file///,如:
<properties resource="file\\\C:\Users\28591\IDEA\myProjects\Spring6\spring-first\src\main\resources\db.properties"/>

配置文件优先级:当一个属性在不止一个地方进行了配置,则MyBatis按照下列顺序进行加载:

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

由此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

2.3typeAliases

  myBatis提供了<typeAliases>来设置类型别名,其含有子标签<typeAlias><package>

  • <typeAlias>:设置类型别名,含有属性type、alias。指定后,可在<select>标签的resultType中使用。
    • type:指定全类名(包路径+类名)。
    • alias:指定别名,不指定时默认使用类名。
<typeAliases>
    <typeAlias type="org.example.Pojo.User" alias="User"/>
</typeAliases>
  • <package>:指定包,包下所有的类都使用全类名作为别名(默认的类型别名)。
<package name="org.example.Pojo.User">

2.4mappers

  <Mappers>标签的作用是用来在核心配置文件里面引入映射文件,引入方式有如下三种:
1.使用resources/url:使用方式同<properties>标签,通过路径引入单个映射文件。

2.批量加载mapper
  使用<package>自动扫描包下的mapper接口进行加载,且以包为单位引入映射文件有如下要求:

  • mapper接口所在的包要与映射文件所在的包一致(虽然创建存放mapper文件是在resources目录下,但在打包时会自动将层级路径相同的文件合并,即,mapper映射文件最终会和mapper接口存放在同一包中)。
  • mapper接口和映射文件的名字一致。
    由于mapper映射文件存放在resources目录下,故此处创建与java目录下有相同结构的包存放mapper接口:
    在这里插入图片描述
    注意,resources不能直接创建软件包,故在创建层级目录时不能像java包一样使用.进行,而需使用/表层级目录。修改核心配置文件:
<!--引入映射文件-->
<mappers>
    <package name="org.example.mapper"/>
</mappers>

3.使用class:使用mapper接口引入单个映射文件,需遵循以下规范:
- mapper接口名需与映射文件名相同。
- mapper接口所在的包要与映射文件所在的包一致(规则同package)。

<!--UserMapper是Mapper接口-->
<mapper class="com.mybatis.mapper.UserMapper"/>

三、MyBatis获取参数的两种方式

  MyBatis获取参数的两种方式为${}#{}

  • ${}:本质是字符串拼接得到sql语句(故存在SQL注入问题),若为字符串类型或日期类型,则需手动添加单引号。
  • #{}:本质是占位符赋值得到sql语句(本质是调用PreparedStatement的set方法进行预编译处理,由于会将{}内的整体作为value,并最后加上单引号,故不会发生SQL注入),若为字符串类型或日期类型,会自动添加单引号。

3.1封装获取SqlSessionUtils工具类

  上文中获取SqlSession对象的操作可封装为工具类,方便进行调用。

package org.example.utils;

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 java.io.IOException;
import java.io.InputStream;

public class SqlSessionUtils {
    public static SqlSession getSqlSession() throws IOException {
        SqlSession sqlSession =null;
        try{//使用try-catch环绕,避免调用方法需抛出异常
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession(true);
        }catch (Exception e){
            e.printStackTrace();
        }
        return sqlSession;
    }
}

3.2参数为单个字面量类型

package org.example.mapper;
import org.example.Pojo.User;
import java.util.List;
public interface UserMapper {
    //通过用户名获取用户对象
    User getUserByUsername(String name);
}

1.使用#{}

<!--User getUserByUsername(String name);-->
<select id="getUserByUsername" resultType="org.example.Pojo.User">
	select * from table_user where name=#{name};
</select>

进行测试:
在这里插入图片描述
可见,Mybatis在处理#{}的时候会将sql中的#{}替换成?号,调用PreparedStatement来赋值(预编译处理)。

2.使用${}

<!--User getUserByUsername(String name);-->
<select id="getUserByUsername" resultType="org.example.Pojo.User">
	select * from table_user where name=${name};
</select>

在这里插入图片描述
  可见,${}的本质是字符串拼接,当参数是字符串类型时应手动加上引号:

<!--User getUserByUsername(String name);-->
<select id="getUserByUsername" resultType="org.example.Pojo.User">
    select * from table_user where name='${name}';
</select>

事实上,Mybatis在处理${}的时候就是把${}替换成变量的值,调用Statement来赋值。

3.3参数为多个字面量类型

  实现验证登录功能:

package org.example.mapper;
import org.example.Pojo.User;
public interface UserMapper {
    //验证登录
    User checkLogin(String name,String password);
}

当有多个参数需进行传递时,MyBatis会将参数包装成一个Map传入,并以两种方式进行存储(可混合使用):

  • 以arg0、arg1、…为键,参数为值。
  • 以param1、param2、…为键,参数为值。

1.使用#{}

<!--User checkLogin(String name,String password);-->
<select id="checkLogin" resultType="org.example.Pojo.User">
    select * from table_user where name=#{arg0} and password=#{arg1};
</select>

2.使用${}

<select id="checkLogin" resultType="org.example.Pojo.User">
    select * from table_user where name='${arg0}' and password='${arg1}';
</select>

3.4Map类型参数传递

  当有多个参数进行传递时,可将参数封装在Map集合中进行传递(上文中是MyBatis自动存放在集合中,此处为手动存放):

package org.example.mapper;

import org.example.Pojo.User;
import java.util.Map;
public interface UserMapper {
    //验证登录
    User checkLoginByMap(Map<String, Object> map);
}

测试类:

@Test
public void testSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map=new HashMap<>();
    map.put("name","张三");
    map.put("password","123456");
    User user=userMapper.checkLoginByMap(map);
    System.out.println(user);
}

1.使用#{}

<!--User checkLoginByMap(Map<String, Object> map);-->
<select id="checkLoginByMap" resultType="org.example.Pojo.User">
    select * from table_user where name=#{name} and password=#{password};
</select>

2.使用${}

<!--User checkLoginByMap(Map<String, Object> map);-->
<select id="checkLoginByMap" resultType="org.example.Pojo.User">
    select * from table_user where name='${name}' and password='${password}';
</select>

3.5实体类类型参数传递

  Map集合以键进行访问,而实体类类型以属性名进行访问,但需注意使用${}时的单引号问题。声明Mapper接口:

package org.example.mapper;

import org.example.Pojo.User;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public interface UserMapper {
    //添加用户信息
    int insertUser(User user);
}

1.使用#{}

<!--int insertUser(User user);-->
<insert id="insertUser">
    insert into table_user values(null,#{name},#{password},#{age},#{sex},#{email})
</insert>

2.使用${}

<!--int insertUser(User user);-->
<insert id="insertUser">
    insert into table_user values(null,'${name}','${password}',#{age},'${sex}','${email}')
</insert>

3.6@Param命名参数

3.6.1基本使用

1.标注基本数据类型

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.User;
public interface UserMapper {
    //验证登录
    User checkLoginByParam(@Param("name") String name, @Param("password") String password);
}

此时MyBatis会自动将这些被注解标注的参数放在Map集合当中,以@Param的值为键,以参数内容为值。
使用#{}

<!--User checkLoginByParam(@Param("name") String name, @Param("password") String password);-->
<select id="checkLoginByParam" resultType="org.example.Pojo.User">
    select * from table_user where name=#{name} and password=#{password}
</select>

使用${}

<!--User checkLoginByParam(@Param("name") String name, @Param("password") String password);-->
<select id="checkLoginByParam" resultType="org.example.Pojo.User">
    select * from table_user where name='${name}' and password='${password}'
</select>

  当使用别的名称访问并报错时可查看到Map的存储方式:
在这里插入图片描述
2.标注JavaBean
  标注JavaBean时可通过注解的值.属性名的方式进行引用:

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.User;
public interface UserMapper {
    //添加用户信息
    int insertUser(@Param("user") User user);
}

Mapper映射文件:

<!--int insertUser(User user);-->
<insert id="insertUser">
    insert into table_user values(null,#{user.name},#{user.password},#{user.age},#{user.sex},#{user.email})
</insert>

3.6.2源码解析

见29集。

四、MyBatis各种查询方法

4.1查询一个实体类对象

  Mapper接口:

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.User;

public interface UserMapper {
    //根据id查询用户信息
    User getUserById(@Param("id") int id);
}

  Mapper映射文件:

<!--User getUserById(@Param("id") int id);-->
<select id="getUserById" resultType="org.example.Pojo.User">
    select * from table_user where id=#{id}
</select>

4.2查询多个实体类对象

  当查询出的数据有多条时,就不可使用实体类对象接收,否则会抛出TooManyResultsException异常。此时,可使用List集合进行接收:

//查询所有用户信息
List<User> getAllUser();

  Mapper映射文件:

<!--List<User> getAllUser();-->
<select id="getAllUser" resultType="org.example.Pojo.User">
    select * from table_user;
</select>

4.3查询字面量

  当查询结果为字面量时,需根据结果类型指定对应的resultType。查询用户表中的总记录数:

package org.example.mapper;
import org.example.Pojo.User;

public interface UserMapper {
    //查询用户总数
    Integer getCount();
}
<!--Integer getCount();-->
<select id="getCount()" resultType="java.lang.Integer">
    select count(*) from table_user;
</select>

  事实上,MyBatis中设置了默认的类型别名:
在这里插入图片描述
直接使用resultType="integer"也是可行的。

4.4查询Map集合

4.4.1将查询出的单行数据转化为Map集合

  将查询结果保存到Map集合中:

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.User;
import java.util.Map;

public interface UserMapper {
    //根据id查询用户信息为一个map集合
    Map<String,Object> getUserById(@Param("id") int id);
}
<!--Map<String,Object> getUserById(@Param("id") int id);-->
<select id="getUserById" resultType="map">
    select * from table_user where id = #{id};
</select>

进行测试:

@Test
public void testSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map=userMapper.getUserById(3);
    System.out.println(map);
}

在这里插入图片描述

4.4.2将查询出的多行数据转化为Map集合

1.使用List存储
  使用List集合存储保存单行数据的多个Map集合:

//查询所有用户信息为Map集合
List<Map<String,Object>> getAllToMap();
<!--List<Map<String,Object>> getAllToMap();-->
<select id="getAllToMap" resultType="map">
    select * from table_user;
</select>

进行测试:

@Test
public void testSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    List<Map<String,Object>> list=userMapper.getAllToMap();
    System.out.println(list);
}

在这里插入图片描述
2.使用注解@MapKey
  用@MapKey注解可以将每条数据转换的map集合作为值,以某个字段的值作为键,放在同一个map集合中。

package org.example.mapper;

import org.apache.ibatis.annotations.MapKey;
import org.example.Pojo.User;
import java.util.Map;
public interface UserMapper {
    //查询所有用户信息为Map集合
    @MapKey("id")   //使用唯一字段id作为键
    Map<String,Object> getAllToMap();
}
<!--Map<String,Object> getAllToMap();-->
<select id="getAllToMap" resultType="map">
    select * from table_user;
</select>

进行测试:

@Test
public void testSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map=userMapper.getAllToMap();
    System.out.println(map);
}

在这里插入图片描述

五、特殊SQL的执行

5.1模糊查询

  模糊查询不可使用#{}

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.User;
import java.util.List;
public interface UserMapper {
    //根据用户名查询用户信息
    List<User> getUserByLike(@Param("name") String name);
}

测试类:

public void testSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    List<User> list=userMapper.getUserByLike("a");
    System.out.println(list);
}

1.使用#{}

<!--List<User> getUserByLike(@Param("name") String name);-->
<select id="getUserByLike" resultType="org.example.Pojo.User">
    select * from table_user where name like '%#{name}%'
</select>

在这里插入图片描述
  使用#{}模糊查询时,由于需要使用通配符%,故需用单引号括住,但也使得#{}被替换为?后无法被识别并替换为对应的值。报错含义为,未检测到SQL语句中有占位符,但却为占位符赋值。

2.使用${}

<!--List<User> getUserByLike(@Param("name") String name);-->
<select id="getUserByLike" resultType="org.example.Pojo.User">
    select * from table_user where name like '%${name}%'
</select>

在这里插入图片描述
  使用${}模糊查询时,虽可正常查询,但造成了SQL安全性的问题,也就是用户可以进行SQL注入。

3.使用concat函数+#{}

<!--List<User> getUserByLike(@Param("name") String name);-->
<select id="getUserByLike" resultType="org.example.Pojo.User">
    select * from table_user where name like concat('%',#{name},'%')
</select>

在这里插入图片描述
  解决了SQL注入且能在配置文件中写"%"通配符的问题。

4.使用concat函数+${}

<!--List<User> getUserByLike(@Param("name") String name);-->
<select id="getUserByLike" resultType="org.example.Pojo.User">
    select * from table_user where name like concat('%','${name}','%')
</select>

在这里插入图片描述
  解决了SQL注入且能在配置文件中写"%"通配符的问题。

5.2批量删除

  

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.User;
public interface UserMapper {
    //根据id进行批量删除
    int deleteMore(@Param("ids") String ids);
}

测试类:

@Test
public void testDelete() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    userMapper.deleteMore("1,2,3");
}

1.使用#{}

<!--int deleteMore(@Param("ids") String ids);-->
<delete id="deleteMore">
    delete from table_user where id in (#{ids})
</delete>

在这里插入图片描述
  #{}会自动给参数加上单引号,此时SQL语句变为

delete from table_user where id in ('1,2,3')

2.使用${}
  ${}本质是字符串拼接,可正常删除:

<!--int deleteMore(@Param("ids") String ids);-->
<delete id="deleteMore">
    delete from table_user where id in (${ids})
</delete>

在这里插入图片描述

5.3动态设置表名

  MySQL的一张表中可能有大量数据,这会影响MySQL的性能,此时可将该表切分为多张表,分表的数据虽都属于原先的表,但表名发生了变化,此时需要动态设置表明名。同理,由于SQL语句的表名无单引号,故不能使用#{}

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.User;
import java.util.List;

public interface UserMapper {
    //查询指定表中的数据
    List<User> getUserByTableName(@Param("tableName") String tableName);
}
<!--List<User> getUserByTableName(@Param("tableName") String tableName);-->
<select id="getUserByTableName" resultType="org.example.Pojo.User">
    select * from ${tableName};
</select>
@Test
public void testSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    List<User> list=userMapper.getUserByTableName("table_user");
    System.out.println(list);
}

在这里插入图片描述

5.4获取添加功能自增的主键

package org.example.mapper;
import org.example.Pojo.User;
public interface UserMapper {
    //获取添加功能自增的主键
    void insertUserGetKey(User user);
}

  由于SQL语句的返回值固定,如insert只能返回受影响的行数,故自动生成的主键只能通过赋给对象的属性来返回。

<insert id="insertUserGetKey" useGeneratedKeys="true" keyProperty="id">
    <!--
        useGeneratedKeys="true":表示使用自动递增的主键。
        keyProperty="id":将自增的主键值赋值给传输到映射文件中参数的某个属性中。此处是将获取的主键放在属性id中。
    -->
    insert into table_user values(null,#{name},#{password},#{age},#{sex},#{email})
</insert>
@Test
public void testInsert() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
    User user=new User(null,"王五","123456",19,"男","123456@qq.com");
    userMapper.insertUserGetKey(user);
    System.out.println("新User的主键id:"+user.getId());
}

在这里插入图片描述

六、自定义映射关系

1.环境准备
创建员工表:

create table table_emp
(
    eid       int auto_increment comment '员工id'
        primary key,
    emp_name  varchar(20) null comment '员工姓名',
    emp_age   int         null comment '员工年龄',
    emp_sex   char        null comment '员工性别',
    emp_email varchar(20) null,
    did       int         null comment '员工所在部门id'
)
    comment '员工表';

在这里插入图片描述
创建对应实体类:

package org.example.Pojo;

public class Emp {
    private Integer eid;
    private String empName;
    private Integer age;
    private String sex;
    private String email;

    public Emp() {
    }

    public Emp(Integer eid, String empName, Integer age, String sex, String email) {
        this.eid = eid;
        this.empName = empName;
        this.age = age;
        this.sex = sex;
        this.email = email;
    }

    public Integer getEid() {
        return eid;
    }

    public void setEid(Integer eid) {
        this.eid = eid;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "eid=" + eid +
                ", empName='" + empName + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

创建Mapper接口:

package org.example.mapper;
public interface EmpMapper {
}

创建Mapper映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.EmpMapper">
</mapper>

创建部门表:

create table table_dept
(
    did       int auto_increment comment '部门id'
        primary key,
    dept_name varchar(20) null comment '部门名称'
);

在这里插入图片描述
创建对应实体类:

package org.example.Pojo;

public class Dept {
    private Integer did;
    private String deptName;

    public Dept() {
    }

    public Dept(Integer did, String deptName) {
        this.did = did;
        this.deptName = deptName;
    }

    public Integer getDid() {
        return did;
    }

    public void setDid(Integer did) {
        this.did = did;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "did=" + did +
                ", deptName='" + deptName + '\'' +
                '}';
    }
}

创建Mapper接口:

package org.example.mapper;

public interface DeptMapper {
}

创建Mapper映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.DeptMapper">
</mapper>

2.使用场景
  实现查询所有员工信息功能:

package org.example.mapper;
import org.example.Pojo.Emp;
import java.util.List;
public interface EmpMapper {
    //查询所有员工信息
    List<Emp> getAllEmp();
}
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultType="org.example.Pojo.Emp">
    select * from table_emp;
</select>

测试类:

@Test
public void testEmpSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class);
    List<Emp> list=empMapper.getAllEmp();
    System.out.println(list);
}

在这里插入图片描述
在得到的List当中,除eid外的属性都未完成赋值,这是因为resultType获取到字段后会将字段名与Emp对象属性名相同的完成赋值,此时就需要使用映射功能。

6.1使用字段别名

  在SQL语句中直接为查询的字段名起与属性名相同的别名:

<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultType="org.example.Pojo.Emp">
    select eid,emp_name empName,emp_age age,emp_sex sex,emp_email email from table_emp;
</select>

6.2使用全局配置mapUnderscoreToCamelCase

  在核心配置文件中使用<settings>标签的属性mapUnderscoreToCamelCase设置MyBatis的全局配置:

<settings>
    <!--将_自动映射为驼峰-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

此时如emp_name字段被查询出时会被赋值到empName属性(本例中并不适用)。注意,转换过程以_为准,如em_pname会被映射为emPname

6.3自定义映射resultMap:一对一映射

  可以使用自定义映射resultMap

<!--type指定映射的实体类对象-->
<resultMap id="empResultMap" type="org.example.Pojo.Emp">
    <!--id设置主键的映射关系-->
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
</resultMap>
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultMap="empResultMap">
    select * from table_emp;
</select>

  resultMap的常用属性:

  • id:唯一标识。
  • type:设置映射关系中的实体类类型。

resultMap常用子标签为idresult,分别用于设置主键、普通字段的映射关系,常用属性为:

  • property:设置映射关系中的属性名,必须是type属性所设置的实体类类型的属性名。
  • column:设置映射关系中的字段名,必须是sql语句查询出的字段名。

6.4自定义映射resultMap:多对一映射

  建立多对一关系,即多个员工可对应一个部门,在Emp类中增加属性dept,重新生成JavaBean(构造器中不含dept属性):

package org.example.Pojo;

public class Emp {
    private Integer eid;
    private String empName;
    private Integer age;
    private String sex;
    private String email;
    private Dept dept;

    public Emp() {
    }

    public Emp(Integer eid, String empName, Integer age, String sex, String email) {
        this.eid = eid;
        this.empName = empName;
        this.age = age;
        this.sex = sex;
        this.email = email;
    }

    public Integer getEid() {
        return eid;
    }

    public void setEid(Integer eid) {
        this.eid = eid;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "eid=" + eid +
                ", empName='" + empName + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                ", dept=" + dept +
                '}';
    }
}

Mapper接口:

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;

public interface EmpMapper {
    //查询员工及员工所对应的部门信息
    Emp getEmpAndDept(@Param("eid") Integer eid);
}

6.4.1级联属性赋值方式

  此处使用左外连接获取部门信息,获取到的diddept_name字段应当映射到Emp对象的diddeptName属性,此时可使用级联的方式:

<resultMap id="empAndDeptResultMap" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
    <result property="dept.did" column="did"/>
    <result property="dept.deptName" column="dept_name"/>
</resultMap>
<!--Emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptResultMap">
    select * from table_emp left join table_dept on table_emp.did = table_dept.did where table_emp.eid=#{eid}
</select>
@Test
public void testEmpSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class);
    Emp emp=empMapper.getEmpAndDept(2);
    System.out.println(emp);
}

在这里插入图片描述

6.4.2association方式

  <association>可用于处理多对一的映射关系,有以下常用属性:

  • property:需要处理的映射关系属性名。
  • javaType:该属性的类型。
<resultMap id="empAndDeptResultMap" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
    <association property="dept" javaType="org.example.Pojo.Dept">
        <id property="did" column="did"/>
        <result property="deptName" column="dept_name"/>
    </association>
</resultMap>
<!--Emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptResultMap">
    select * from table_emp left join table_dept on table_emp.did = table_dept.did where table_emp.eid=#{eid}
</select>

6.4.3分步查询方式

  先查询员工,通过员工对应的部门did查询部门信息,共包含两条SQL语句。
1.查询员工信息

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;
public interface EmpMapper {
    //分步查询查询员工及对应的部门信息:1.查询员工信息
    Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
}
<resultMap id="getEmpAndDeptByStepOne" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
    <!--获取getEmpAndDeptByStepTwo对应SQL查询出的结果进行赋值-->
    <association property="dept" select="org.example.mapper.DeptMapper.getEmpAndDeptByStepTwo" column="did"/>
</resultMap>
<!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStepOne">
    select * from table_emp where eid=#{eid}
</select>

其中,select用于引用Dept映射文件中的SQL语句,即设置分步查询的sql唯一标识(namespace.SQLId或mapper接口的全类名.方法名),而column用于设置分步查询的条件,即将当前Emp对象的did属性作为参数传递给下一条SQL语句。
2.通过员工部门id获取部门信息

package org.example.mapper;

import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Dept;

public interface DeptMapper {
    //分步查询查询员工及对应的部门信息:2.查询部门信息
    Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);
}

Dept对应的Mapper映射文件:

<resultMap id="getEmpAndDeptByStepTwo" type="org.example.Pojo.Dept">
    <id property="did" column="did"/>
    <result property="deptName" column="dept_name"/>
</resultMap>
<!--Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
<select id="getEmpAndDeptByStepTwo" resultMap="getEmpAndDeptByStepTwo">
    select * from table_dept where did=#{did}
</select>

进行测试:

public void testEmpSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class);
    Emp emp=empMapper.getEmpAndDeptByStepOne(2);
    System.out.println(emp);
}

在这里插入图片描述
  可获取部门did,需二次查询获取部门名称。

6.4.4延迟加载

  分步查询的好处在于可以设置延迟加载,上例中查询员工信息时先根据eid查询员工信息,再根据Emp对象did属性查询部门信息,启动延迟加载后,若只需访问员工信息,则只会执行第一条SQL语句,只有当访问员工对应部门信息时才会执行第二条SQL。需在核心配置文件中设置全局配置信息,启动延迟加载:

  • lazyLoadingEnabled:延迟加载的全局开关,默认关闭,在开启时所有关联对象都会延迟加载。
<settings>
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>
@Test
public void testEmpSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class);
    Emp emp=empMapper.getEmpAndDeptByStepOne(2);
    System.out.println(emp.getEmpName());
}

在这里插入图片描述
  此时只执行访问员工信息的SQL,而当访问员工的dept属性时就会执行两条SQL:
在这里插入图片描述
即,访问dept属性时才执行第二条SQL,否则不执行。
  对于不想开启延迟加载的分步查询,也可通过fetchType进行设置:

<resultMap id="getEmpAndDeptByStepOne" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
    <!--获取getEmpAndDeptByStepTwo对应SQL查询出的结果进行赋值-->
    <association property="dept" select="org.example.mapper.DeptMapper.getEmpAndDeptByStepTwo" column="did" fetchType="eager"/>
</resultMap>
<!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStepOne">
    select * from table_emp where eid=#{eid}
</select>

6.5一对多映射

  一个部门中含有多个员工,为部门类Dept增加List属性emps:

package org.example.Pojo;

import java.util.List;

public class Dept {
    private Integer did;
    private String deptName;
    private List<Emp> emps;

    public Dept() {
    }

    public Dept(Integer did, String deptName) {
        this.did = did;
        this.deptName = deptName;
    }

    public Integer getDid() {
        return did;
    }

    public void setDid(Integer did) {
        this.did = did;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public List<Emp> getEmps() {
        return emps;
    }

    public void setEmps(List<Emp> emps) {
        this.emps = emps;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "did=" + did +
                ", deptName='" + deptName + '\'' +
                ", emps=" + emps +
                '}';
    }
}

Mapper接口:

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Dept;
public interface DeptMapper {
    //获取部门及部门中所有员工信息
    Dept getDeptAndEmp(@Param("did") Integer did);
}

测试类:

@Test
public void testDeptSelect() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    DeptMapper deptMapper=sqlSession.getMapper(DeptMapper.class);
    Dept dept=deptMapper.getDeptAndEmp(2);
    System.out.println(dept);
}

6.5.1collection标签

  <collection>标签属于<resultMap>子标签,当Bean对象中有集合类型的属性需要关联查询时就需要使用<collection>标签。注意,对于Emp对象的dept属性不应再赋值,否则会发生嵌套:

<resultMap id="getDeptAndEmp" type="org.example.Pojo.Dept">
    <id property="did" column="did"/>
    <result property="deptName" column="dept_name"/>
    <!--collection表示当前属性是集合类型,ofType表示该属性所对应集合中存储数据的类型-->
    <collection property="emps" ofType="org.example.Pojo.Emp">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="emp_age"/>
        <result property="sex" column="emp_sex"/>
        <result property="email" column="emp_email"/>
    </collection>
</resultMap>
<!--Dept getDeptAndEmp(@Param("did") Integer did);-->
<select id="getDeptAndEmp" resultMap="getDeptAndEmp">
    select * from table_dept left join table_emp on table_dept.did=table_emp.did where table_dept.did=#{did}
</select>

在这里插入图片描述

6.5.2分步查询

1.查询部门信息

package org.example.mapper;

import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Dept;

public interface DeptMapper {
    //获取部门及部门中所有员工信息
    Dept getDeptAndEmp(@Param("did") Integer did);
    /**
     * 通过分步查询获取部门及部门中所有员工的信息
     *      分步查询:1.查询部门信息
     */
    Dept getDeptAndEmpByStepOne(@Param("did") Integer did);
}
<resultMap id="getDeptAndEmpByStepOne" type="org.example.Pojo.Dept">
    <id property="did" column="did"/>
    <result property="deptName" column="dept_name"/>
    <!--did作为传递的条件-->
    <collection property="emps" select="org.example.mapper.EmpMapper.getDeptAndEmpByStepTwo" column="did"/>
</resultMap>
<!--Dept getDeptAndEmpByStepOne(@Param("did") Integer did);-->
<select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOne">
    select * from table_dept where did = #{did}
</select>

2.查询员工信息

package org.example.mapper;

import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;
import java.util.List;
public interface EmpMapper {
    /**
     * 通过分步查询获取部门及部门中所有员工的信息
     *      分步查询:2.根据did查询员工信息
     */
    List<Emp> getDeptAndEmpByStepTwo(@Param("id") Integer did);
}
<!--List<Emp> getDeptAndEmpByStepTwo(@Param("id") Integer did);-->
<select id="getDeptAndEmpByStepTwo" resultType="org.example.Pojo.Emp">
    select * from table_emp where did=#{did}
</select>

进行测试:

SqlSession sqlSession= SqlSessionUtils.getSqlSession();
DeptMapper deptMapper=sqlSession.getMapper(DeptMapper.class);
Dept dept=deptMapper.getDeptAndEmpByStepOne(2);
System.out.println(dept);

在这里插入图片描述

七、动态SQL

  MyBatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,其存在的意义是为了解决拼接SQL语句字符串时的痛点。创建接口DynamicSQL

package org.example.mapper;

public interface DynamicSQL {
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.DynamicSQL">
</mapper>

7.1if:单条件分支条件判断

  if标签可通过test属性的表达式进行判断,若表达式结果为true,则标签中的内容会被拼接到原SQL中。语法格式:

<if test="判断条件">SQL语句</if>

实现多条件查询功能:

package org.example.mapper;

import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;
import java.util.List;
public interface DynamicSQLMapper {
    //多条件查询
    List<Emp> getEmpByCondition(@Param("emp") Emp emp);
}

  在test判断语句中可直接使用传递来的形参进行判断:

 <resultMap id="getEmpByConditionResultMap" type="org.example.Pojo.Emp">
     <id property="eid" column="eid"/>
     <result property="empName" column="emp_name"/>
     <result property="age" column="emp_age"/>
     <result property="sex" column="emp_sex"/>
     <result property="email" column="emp_email"/>
 </resultMap>
 <!--List<Emp> getEmpByCondition(Emp emp);-->
 <select id="getEmpByCondition" resultMap="getEmpByConditionResultMap">
     select * from table_emp where
     <!--当实体类empName不为空或空字符串时,按empName进行查询-->
     <if test="emp.empName != null and emp.empName != ''">
         emp_name=#{emp.empName}
     </if>
     <if test="emp.age !=null and emp.age!= ''">
         and emp_age=#{emp.age}
     </if>
     <if test="emp.sex !=null and emp.sex!= ''">
         and emp_sex=#{emp.sex}
     </if>
     <if test="emp.email !=null and emp.email!= ''">
         and emp_email=#{emp.email}
     </if>
 </select>

若直接使用emp的属性,则报错:
在这里插入图片描述
可见,MyBatis自动将传递来的形参保存在了Map集合当中。当传入Emp对象的empName属性为null或""时,会拼接错误的SQL语句:
在这里插入图片描述
可改成如下写法(加上恒成立的条件):

<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultMap="getEmpByConditionResultMap">
    select * from table_emp where 1=1
    <!--当实体类empName不为空或空字符串时,按empName进行查询-->
    <if test="emp.empName != null and emp.empName != ''">
        emp_name=#{emp.empName}
    </if>
    <if test="emp.age !=null and emp.age!= ''">
        and emp_age=#{emp.age}
    </if>
    <if test="emp.sex !=null and emp.sex!= ''">
        and emp_sex=#{emp.sex}
    </if>
    <if test="emp.email !=null and emp.email!= ''">
        and emp_email=#{emp.email}
    </if>
</select>

7.2where标签

  上述案例中的问题可使用<where>进行解决。<where>可动态生成where关键字,即无条件成立时不生成where关键字,且可将多余的关键字,如and、or去掉。

<resultMap id="getEmpByConditionResultMap" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
</resultMap>
<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultMap="getEmpByConditionResultMap">
    select * from table_emp
    <where>
        <if test="emp.empName != null and emp.empName != ''">
            emp_name=#{emp.empName}
        </if>
        <if test="emp.age !=null and emp.age!= ''">
            and emp_age=#{emp.age}
        </if>
        <if test="emp.sex !=null and emp.sex!= ''">
            and emp_sex=#{emp.sex}
        </if>
        <if test="emp.email !=null and emp.email!= ''">
            and emp_email=#{emp.email}
        </if>
    </where>
</select>

注意,where标签只能去除首位多余的and/or,以下写法就会出错:

<select id="getEmpByCondition" resultMap="getEmpByConditionResultMap">
    select * from table_emp
    <where>
        <if test="emp.empName != null and emp.empName != ''">
            emp_name=#{emp.empName} and
        </if>
        <if test="emp.age !=null and emp.age!= ''">
            emp_age=#{emp.age} and
        </if>
        <if test="emp.sex !=null and emp.sex!= ''">
            emp_sex=#{emp.sex} and
        </if>
        <if test="emp.email !=null and emp.email!= ''">
            emp_email=#{emp.email}
        </if>
    </where>
</select>

在这里插入图片描述

7.3trim标签

  当<trim>中有内容是时会执行其四个属性的功能,而没有内容时就不会有任何效果。属性如下:

属性名功能
suffixOverrides后缀重写,将trim标签中内容后面去除指定内容
suffix后缀,将trim标签中内容后面添加指定内容
prefix前缀,将trim标签中内容前面添加指定内容
prefixOverrides前缀重写,将trim标签中内容前面去除指定内容

  使用prefix、suffixOverrides,此时当<trim>下有内容拼接时会自动加上where,且当拼接的SQL以and/or起始时会自动去除:

<resultMap id="getEmpByConditionResultMap" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
</resultMap>
<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultMap="getEmpByConditionResultMap">
    select * from table_emp
    <trim prefix="where" suffixOverrides="and|or">
        <if test="emp.empName != null and emp.empName != ''">
            emp_name=#{emp.empName} and
        </if>
        <if test="emp.age !=null and emp.age!= ''">
            emp_age=#{emp.age} and
        </if>
        <if test="emp.sex !=null and emp.sex!= ''">
            emp_sex=#{emp.sex} and
        </if>
        <if test="emp.email !=null and emp.email!= ''">
            emp_email=#{emp.email}
        </if>
    </trim>
</select>

7.4choose、when、otherwise

 &emspp;<choose><when><otherwise>相当于if...else if...else。其中,<choose>是父标签,而<when><otherwise>是子标签。注意,与</if>不同,<choose><when><otherwise>只需满足一个就不会继续向下判断,也就无需使用and/or。

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;
public interface DynamicSQLMapper {
    //测试choose、when、otherwise
    Emp getEmpByChoose(@Param("emp") Emp emp);
}
<resultMap id="getEmpByChoose" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
</resultMap>
<!--Emp getEmpByChoose(@Param("emp") Emp emp);-->
<select id="getEmpByChoose" resultMap="getEmpByChoose">
    select * from table_emp
    <where>
        <choose>
            <when test="emp.empName!=null and emp.empName!=''">
                emp_name=#{emp.empName}
            </when>
            <when test="emp.age!=null and emp.age!=''">
                emp_age=#{emp.age}
            </when>
            <when test="emp.sex!=null and emp.sex!=''">
                emp_sex=#{emp.sex}
            </when>
            <when test="emp.email!=null and emp.email!=''">
                emp_email=#{emp.email}
            </when>
            <otherwise>
                <!--都不满足时查询did为1的员工信息-->
                did=1
            </otherwise>
        </choose>
    </where>
</select>
@Test
public void testDynamicSQL() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    DynamicSQLMapper dynamicSQL=sqlSession.getMapper(DynamicSQLMapper.class);
    Emp emp=dynamicSQL.getEmpByChoose(new Emp(null,"张三",null,"男",null));
    System.out.println(emp);
}

在这里插入图片描述

7.5foreach标签

  <foreach>用于迭代传入的集合对象,具有以下属性:

在这里插入图片描述
  在传入参数时可使用@Param注解自定义集合名称,否则,对于List类型集合默认使用list作为集合名,array数组默认使用array作为集合名,而当传入Map集合时,可根据遍历需求进行指定。

7.5.1批量删除:Array实现

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;

public interface DynamicSQLMapper {
    //通过数组批量删除
    Integer deleteMoreByArray(@Param("eids") Integer[] eids);
}

1.使用in完成批量删除

<!--Integer deleteMoreByArray(@Param("eids") Integer[] eids);-->
<delete id="deleteMoreByArray">
    delete from table_emp where eid in
    <foreach collection="eids" item="eid" separator="," open="(" close=")">
        #{eid}
    </foreach>
</delete>
@Test
public void testDeleteMoreByArray() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    DynamicSQLMapper dynamicSQL=sqlSession.getMapper(DynamicSQLMapper.class);
    dynamicSQL.deleteMoreByArray(new Integer[]{1,2,3});
}

在这里插入图片描述

2.使用or完成批量删除

<!--Integer deleteMoreByArray(@Param("eids") Integer[] eids);-->
<delete id="deleteMoreByArray">
    delete from table_emp where eid in
    <foreach collection="eids" item="eid" separator="or" open="(" close=")">
        eid=#{eid}
    </foreach>
</delete>
@Test
public void testDeleteMoreByArray() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    DynamicSQLMapper dynamicSQL=sqlSession.getMapper(DynamicSQLMapper.class);
    dynamicSQL.deleteMoreByArray(new Integer[]{1,2,3});
}

在这里插入图片描述

7.5.2批量添加:List实现

package org.example.mapper;

import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;
import java.util.List;

public interface DynamicSQLMapper {
    //通过列表完成批量添加
    int insertMoreByList(@Param("emps") List<Emp> emps);
}
<!--int insertMoreByList(@Param("emps") List<Emp> emps);-->
<insert id="insertMoreByList">
    insert into table_emp values
    <foreach collection="emps" item="emp" separator=",">
        <!--需先访问集合成员,通过成员get()方法获取属性-->
        (null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
    </foreach>
</insert>
@Test
public void testInsertMoreByList() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    DynamicSQLMapper dynamicSQL=sqlSession.getMapper(DynamicSQLMapper.class);
    Emp emp1=new Emp(null,"a1",23,"男","123@qq.com");
    Emp emp2=new Emp(null,"a2",23,"男","123@qq.com");
    Emp emp3=new Emp(null,"a3",23,"男","123@qq.com");
    List<Emp> list=Arrays.asList(emp1,emp2,emp3);
    dynamicSQL.insertMoreByList(list);
}

注意,不能使用 open="(" close=")",因为在插入语句中需对每一个插入内容加上(),而非删除语句中为整体加上()
在这里插入图片描述

7.5.3批量查询:Map实现

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;
import java.util.List;
import java.util.Map;
public interface DynamicSQLMapper {
    //通过Map完成批量查询
    List<Emp> selectMoreByMap(@Param("empMap") Map<String,Integer> emp);
}

1.通过键值对查询

<resultMap id="empResultMap" type="org.example.Pojo.Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="age" column="emp_age"/>
    <result property="sex" column="emp_sex"/>
    <result property="email" column="emp_email"/>
</resultMap>
<!--List<Emp> selectMoreByMap(@Param("empMap") Map<String,Integer> emp);-->
<select id="selectMoreByMap" resultMap="empResultMap">
    select * from table_emp where (emp_name,emp_age) in
    <foreach collection="empMap.entrySet()" item="value" index="key" separator="," open="(" close=")">
        (#{key},#{value})
    </foreach>
</select>
@Test
public void selectMoreByMap() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    DynamicSQLMapper dynamicSQL=sqlSession.getMapper(DynamicSQLMapper.class);
    Map<String,Integer> empMap=new HashMap<>();
    empMap.put("李四",22);
    empMap.put("王五",19);
    empMap.put("张三",99);
    System.out.println(dynamicSQL.selectMoreByMap(empMap));
}

在这里插入图片描述
2.通过键查询

<!--List<Emp> selectMoreByMap(@Param("empMap") Map<String,Integer> emp);-->
<select id="selectMoreByMap" resultMap="empResultMap">
    select * from table_emp where emp_name in
    <foreach collection="empMap.keys" item="key" separator="," open="(" close=")">
        #{key}
    </foreach>
</select>

3.通过值查询

<!--List<Emp> selectMoreByMap(@Param("empMap") Map<String,Integer> emp);-->
<select id="selectMoreByMap" resultMap="empResultMap">
    select * from table_emp where emp_age in
    <foreach collection="empMap.values" item="value" separator="," open="(" close=")">
        #{value}
    </foreach>
</select>

7.6SQL片段

  <sql>标签用于将常用的SQL语句设置为SQL片段,之后可通过<include>标签直接进行引用,减少了SQL语句的冗余使用。

package org.example.mapper;
import org.example.Pojo.Emp;
import java.util.List;
public interface DynamicSQLMapper {
    //查询所有员工信息
    List<Emp> getAllEmp();
}
<sql id="empColumns">eid,emp_name,emp_age,emp_sex,emp_email,did</sql>
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultMap="empResultMap">
    select <include refid="empColumns"/> from table_emp;
</select>

八、缓存

  mybatis为减轻数据库压力,提高数据库性能。提供了两级缓存机制。

8.1一级缓存机制

  一级缓存是SqlSession级别,通过同一个SqlSession查询的数据会被缓存(对象中有基于PerpetualCache的HashMap本地缓存数据结构存储数据,不同SqlSession之间的缓存数据区域互不影响),下次查询相同数据时,会从缓存直接获取,而无需重新访问数据库。使一级缓存失效的四种情况:

  • 不同的SqlSession对应不同的一级缓存。
  • 同一个SqlSession但是查询条件不同。
  • 同一个SqlSession两次查询期间执行了任何一次增删改操操作,此时MyBatis会清空当前SqlSession缓存中的所有缓存数据,保证缓存中存的数据和数据库中一致。
  • 同一个SqlSession两次查询期间手动清空了缓存。

注意,一级缓存是MyBatis自动开启的,无法进行关闭,但可手动清除缓存。

package org.example.mapper;
import org.apache.ibatis.annotations.Param;
import org.example.Pojo.Emp;
public interface CacheMapper {
    //通过id查询员工信息
    Emp getEmpById(@Param("eid") Integer eid);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.CacheMapper">
    <resultMap id="empResultMap" type="org.example.Pojo.Emp">
        <!--id设置主键的映射关系-->
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="emp_age"/>
        <result property="sex" column="emp_sex"/>
        <result property="email" column="emp_email"/>
    </resultMap>

    <!--Emp getEmpById(@Param("eid") Integer eid);-->
    <select id="getEmpById" resultMap="empResultMap">
        select * from table_emp where eid=#{eid}
    </select>
</mapper>
@Test
public void cacheTest() throws IOException {
    SqlSession sqlSession= SqlSessionUtils.getSqlSession();
    CacheMapper cacheMapper=sqlSession.getMapper(CacheMapper.class);
    Emp emp1=cacheMapper.getEmpById(1);
    System.out.println(emp1);
    Emp emp2=cacheMapper.getEmpById(1);
    System.out.println(emp2);
}

在这里插入图片描述
可见,仅在查询第一条数据时创建连接、执行SQL语句,对于第二条查询语句的数据是直接从缓存当中获取。而将代码改为:

@Test
public void cacheTest() throws IOException {
    SqlSession sqlSession1= SqlSessionUtils.getSqlSession();
    CacheMapper cacheMapper1=sqlSession1.getMapper(CacheMapper.class);
    Emp emp1=cacheMapper1.getEmpById(1);
    System.out.println(emp1);
    SqlSession sqlSession2= SqlSessionUtils.getSqlSession();
    CacheMapper cacheMapper2=sqlSession2.getMapper(CacheMapper.class);
    Emp emp2=cacheMapper2.getEmpById(1);
    System.out.println(emp2);
}

就需要执行两条SQL语句完成查询。

8.2二级缓存

  二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,此后若再次执行相同的SQL语句,会从缓存中获取。二级缓存开启的条件:

  • 核心配置文件中设置全局配置(<settings>用于设置全局配置)属性cacheEnabled="true"(默认为true,无需设置)。
  • 映射文件中设置<cache>标签。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.CacheMapper">
    <cache/>
</mapper>
  • 数据在在SqlSession关闭或提交后才会保存到二级缓存中,否则只有一级缓存有效。
  • 查询的数据所转换的实体类类型必须实现序列化的接口Serializable

  先前创建的工具类中,每调用一次方法都会新建SqlSessionFactory对象,故不再使用工具类(Emp类需实现Serializable接口):

@Test
public void cacheTest() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
    CacheMapper cacheMapper1=sqlSession1.getMapper(CacheMapper.class);
    System.out.println(cacheMapper1.getEmpById(1));
    //提交sqlSession,使二级缓存生效
    sqlSession1.commit();
    CacheMapper cacheMapper2=sqlSession2.getMapper(CacheMapper.class);
    System.out.println(cacheMapper2.getEmpById(1));
}

在这里插入图片描述
由于二级缓存是SqlSessionFactory级别,故SqlSession.clearCache()只会影响一级缓存,而不会清空二级缓存。

8.3二级缓存的相关配置

在这里插入图片描述

8.4缓存查询顺序

在这里插入图片描述

九、分页插件

9.1配置方法

  引入依赖:

<dependency>
   <groupId>com.github.pagehelper</groupId>
   <artifactId>pagehelper</artifactId>
   <version>6.0.0</version>
</dependency>

在核心配置文件中配置分页插件:

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

9.2进行测试

  分页功能是指在查询语句后使用关键字limit,并使用以下参数:

  • index:当页的起始索引。
  • pageSize:每页显示的条数。
  • pageNum:当前页的页码。

此时有index=(pageNum-1)*pageSize

复习完数据库再看了:尚硅谷

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# Secs源码 HsmsSecs测试

包含客户端和服务端 启动客户端和服务端即可互相模拟sece 通讯 也可使用secs仿真器进行测试 开启后进行相关操作&#xff0c;创建客户端连接仿真器进行操作 仿真器显示日志 相关文件&#xff0c;源码 4.9 私信即可或者看我博客描述那个地址 我是狗子&#xff0c;希望你幸…

Vue47-修改默认配置webpack.config.js文件

main.js是脚手架项目的入口文件&#xff0c;系统运行时&#xff0c;默认去找src下的main.js文件。这是webpack通过配置文件&#xff1a;webpack.config.js配置的。 脚手架把所有重要的配置文件都隐藏了&#xff0c;方式被开发者修改。 一、查看被隐藏的webpack配置 1-1、webpa…

Matlab进阶绘图第60期—带伪彩图的曲面图

带伪彩图的曲面图是曲面图与伪彩图的组合。 其中&#xff0c;伪彩图与曲面图的颜色用于表示同一个特征。 由于伪彩图无遮挡但不直观&#xff0c;曲面图直观但有遮挡&#xff0c;而将二者组合&#xff0c;可以实现优势互补。 本期就来分享一下带伪彩图的曲面图的绘制方法&…

韩兴国/姜勇团队在《Trends in Plant Science》发表植物根系氮素再分配的观点文章!

氮素是陆地生态系统中的关键限制性营养元素&#xff0c;通过生物固氮和土壤氮供应通常远低高等植物的氮需求。当土壤氮素供应无法充分满足植物茎叶生长需求时&#xff0c;植物会通过自身营养器官&#xff08;如根或根茎&#xff09;再分配来实现氮的内部循环和再利用。尽管植物…

App端接口用例设计方法和测试方法

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 前言 接口测试作为测试的重要一环&#xff0c;重点关注的是数据层面的输入输出&#xff0c;今天…

API接口设计的艺术:如何提升用户体验和系统性能

在数字时代&#xff0c;API接口的设计对于用户体验和系统性能有着至关重要的影响。良好的设计可以显著提升应用程序的响应速度、可靠性和易用性。以下是几个关键点&#xff0c;帮助改善API接口的设计&#xff1a; 1. 理解并定义清晰的要求 用户研究&#xff1a;与最终用户进行…

CesiumJS【Basic】- #006 浏览器控制台查看位置角度

文章目录 浏览器控制台查看位置角度1 目标 浏览器控制台查看位置角度 1 目标 浏览器控制台查看位置角度

详情资料SR560(斯坦福)SR570 低噪声前置放大器

SR560 低噪声前置放大器 SR560 是一款高性能、低噪声前置放大器&#xff0c;非常适合各种应用&#xff0c;包括低温测量、光学检测和音频工程。 输入 SR560 有一个差分前端&#xff0c;输入噪声为 4 nV/√Hz&#xff0c;输入阻抗为 100 MΩ。完整的噪声系数轮廓如下图所示。…

【笔记】复制Edge的网址粘贴后自动变成中文标题超链接

问题 1、从edge复制的网址粘贴直接显示网页内容名称而不是网址url。 2、复制任何网址粘贴到CSDN里面粘贴时直接转换成标题超链接&#xff08;很讨厌的功能习惯&#xff09;。 而如上两种问题不是互相影响的&#xff0c;就算设置了Edge的粘贴方式&#xff0c;复制到CSDN的文章…

【机器学习300问】120、该怎么用RNN来构建语言模型?

一、基本概念补充 在构建语言模型之前补充几个自然语言处理&#xff08;NLP&#xff09;基本概念。 &#xff08;1&#xff09;语料库&#xff08;Corpus&#xff09; ① 语料库的定义 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;语料库是一个经过组织和加工…

自定义starter并发布maven私服

一、搭建nexus私服 nexus就是maven的私有服务器&#xff0c;这个搭建教程可以在网络上找到很多&#xff0c;这里就不赘述了。搭建完成之后再进行下一步 二、本地maven的setting配置文件中配置nexus的用户名和密码 <servers><server><id>nexus-releases<…

如何拥有自己的微信小程序

如何拥有自己的微信小程序 ~~话先放在这里~~ 写在前面申请一个属于自己的小程序先去[微信开放平台](https://open.weixin.qq.com/home)申请一个你的小程序扫码申请新小程序小程序该记好的个人信息 安装微信开发者工具下载工具关联你的小程序请求域名配置发布小程序 BUY一个自己…

抛光粉尘可爆性检测 打磨粉尘喷砂粉尘爆炸下限测试

抛光粉尘可爆性检测 抛光粉尘的可爆性检测是一种安全性能测试&#xff0c;用于确定加工过程中产生的粉尘在特定条件下是否会爆炸&#xff0c;从而对生产安全构成威胁。如果粉尘具有可爆性&#xff0c;那么在生产环境中就需要采取相应的防爆措施。粉尘爆炸的条件通常包括粉尘本身…

大模型应用开发技术:Multi-Agent框架流程、源码及案例实战(一)

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

YOLOv10涨点改进轻量化双卷积DualConv,完成涨点且计算量和参数量显著下降

本文独家改进:双卷积由组卷积和异构卷积组成,执行3x3 和 1x1 卷积运算Q代替其他卷积核仅执行 1x1 卷积。 DualIConv 显着降低了深度神经网络的计算成本和参数数量,同时在某些情况下令人惊讶地实现了比原始模型略高的精度。 我们使用 DualConv 将轻量级 MobileNetV2 的参数数量…

11.5.k8s中pod的调度-cordon,drain,delete

目录 一、概念 二、使用 1.cordon 停止调度 1.1.停止调度 1.2.解除恢复 2.drain 驱逐节点 2.1.驱逐节点 2.2.参数介绍 2.3.解除恢复 3.delete 删除节点 一、概念 cordon节点&#xff0c;drain驱逐节点&#xff0c;delete 节点&#xff0c;在对k8s集群节点执行维护&am…

swagger美化

参考资料 https://doc.xiaominfo.com/docs/quick-start/start-knife4j-version 版本选择 需要先确认springboot版本&#xff0c;再确认swagger版本是2还是3&#xff0c;最后还要关注Springfox的版本。 确认springboot版本的方法 简单的看当前项目使用的就行 例子 https:…

算法体系-19 第十九节 暴力递归到动态规划

一 动画规划的概念 优化出现重复解的递归 一旦写出递归来&#xff0c;改动态规划就很快 尝试策略和状态转移方程是一码事 学会尝试是攻克动态规划最本质的能力 如果你发现你有重复调用的过程&#xff0c;动态规划在算过一次之后把答案记下来&#xff0c;下回在越到重复调用过程…

618必抢清单:内存升级国货更强,DDR5劲爆大白菜

随着数字时代的加速发展&#xff0c;平时在PC上玩游戏、办公的时候&#xff0c;电脑性能对我们的影响越来越打。相比于CPU、显卡等硬件&#xff0c;内存对于电脑性能的重要性也很高&#xff0c;而且可以通过更低的成本来升级。内存就像是电脑的神经网络&#xff0c;影响着CPU数…

经验分享,xps格式转成pdf格式

XPS 是一种电子文档格式、后台打印文件格式和页面描述语言。有时候微软默认打印机保存的是xps格式&#xff0c;我们如何转换为pdf格式呢&#xff0c;这里分享一个免费好用的网站&#xff0c;可以实现。 网站&#xff1a;https://xpstopdf.com/zh/ 截图&#xff1a;