• 【MyBatis】MyBatis查询数据库



    引言:

    经过前⾯文章的学习,咱们 Spring 系列的基本操作已经实现的差不多了,接下来,让我们来一起学习更重要的知识——将前端传递的数据存储起来,或者查询数据库⾥⾯的数据。

    1、MyBatis 是什么?

          MyBatis 是⼀款优秀的ORM(对象关系映射)持久层框架,它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了几乎所有的 JDBC代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注解来配置和 映射原始类型、接⼝和 Java POJO(Plain Old Java Objects,普通⽼式 Java 对象)为数据库中的记录。MyBatis官网

    简单来说: MyBatis 是更简单完成程序和数据库交互的⼯具,也就是更简单的操作和读取数据库⼯具。

    2、为什么要学习 MyBatis?

    对于后端开发来说,程序是由以下两个重要的部分组成的:

    1. 后端程序
    2. 数据库

    在这里插入图片描述
    ⽽这两个重要的组成部分要通讯,就要依靠数据库连接⼯具,那数据库连接⼯具有哪些❓ 比如之前我们学习的 JDBC,还有今天我们将要介绍的 MyBatis,那么已经有了 JDBC 了,为什么还要学习 MyBatis呢❓
    这是因为 JDBC 的操作太繁琐了,我们先来回顾⼀下 JDBC 的操作流程:

    1. 创建数据库连接池 DataSource
    2. 通过 DataSource 获取数据库连接 Connection
    3. 编写要执行带 ? 占位符的 SQL 语句
    4. 通过 Connection 及 SQL 创建操作命令对象 Statement
    5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
    6. 使⽤ Statement 执行SQL 语句
    7. 查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
    8. 处理结果集
    9. 释放资源

    从上述操作流程可以看出,对于 JDBC 来说,整个操作⾮常的繁琐,我们不但要拼接每⼀个参数,⽽且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关闭连接等,⽽所有的这些操作步骤都需要在每个⽅法中重复书写。

    于是我们就想,那有没有⼀种方法,可以更简单、更方便的操作数据库呢?

    答案是肯定的,这就是我们要学习 MyBatis 的真正原因,它可以帮助我们更方便、更快速的操作数据库。

    3、怎么学MyBatis?

    MyBatis 学习只分为两部分:

    • 配置 MyBatis 开发环境;
    • 使用 MyBatis 模式和语法操作数据库。

    4、创建MyBatis项目

    开始搭建 MyBatis 之前,我们先来看⼀下 MyBatis 在整个框架中的定位,框架交互流程图:
    在这里插入图片描述
    MyBatis 也是⼀个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射。在⾯向对
    象编程语⾔中,将关系型数据库中的数据与对象建⽴起映射关系,进⽽⾃动的完成数据与对象的
    互相转换:

    1. 将输⼊数据(即传⼊对象)+SQL 映射成原⽣ SQL
    2. 将结果集映射为返回对象,即输出对象

    ORM 把数据库映射为对象:

    • 数据库表(table)–> 类(class)
    • 记录(record,⾏数据)–> 对象(object)
    • 字段(field)–> 对象的属性(attribute)

    ⼀般的 ORM 框架,会将数据库模型的每张表都映射为⼀个 Java 类。

    也就是说使⽤ MyBatis 可以像操作对象⼀样来操作数据库中的表,可以实现对象和数据库表之间 的转换,接下来我们来看 MyBatis 的使⽤吧。

    4.1 创建数据库和表

    接下来我们要实现的功能是:使⽤ MyBatis 的⽅式来读取⽤户表中的所有⽤户,我们使⽤个⼈博
    客的数据库和数据包,具体 SQL 如下:

    -- 创建数据库
    drop database if exists mycnblog;
    create database mycnblog DEFAULT CHARACTER SET utf8mb4;
    
    -- 使用数据数据
    use mycnblog;
    
    -- 创建表[用户表]
    drop table if exists  userinfo;
    create table userinfo(
        id int primary key auto_increment,
        username varchar(100) not null,
        password varchar(32) not null,
        photo varchar(500) default '',
        createtime datetime default now(),
        updatetime datetime default now(),
        `state` int default 1
    ) default charset 'utf8mb4';
    
    -- 创建文章表
    drop table if exists  articleinfo;
    create table articleinfo(
        id int primary key auto_increment,
        title varchar(100) not null,
        content text not null,
        createtime datetime default now(),
        updatetime datetime default now(),
        uid int not null,
        rcount int not null default 1,
        `state` int default 1
    )default charset 'utf8mb4';
    
    -- 创建视频表
    drop table if exists videoinfo;
    create table videoinfo(
      	vid int primary key,
      	`title` varchar(250),
      	`url` varchar(1000),
    		createtime datetime default now(),
    		updatetime datetime default now(),
      	uid int
    )default charset 'utf8mb4';
    
    -- 添加一个用户信息
    INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES 
    (1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);
    
    -- 文章添加测试数据
    insert into articleinfo(title,content,uid)
        values('Java','Java正文',1);
        
    -- 添加视频
    insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    4.2 添加MyBatis相关依赖

    4.2.1 新项目添加mybatis依赖

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

    4.2.2 老项目添加mybatis依赖

    首先请参考➡️ 准备工作—添加 lombok 到项目中
    然后再进行下面的配置:
    在这里插入图片描述
    添加好依赖以后千万不要立即启动项目❗❗千万不要❗❗❗

    4.3 配置数据库连接字符串和MyBatis(保存的XML的目录)

    4.3.1 配置数据库的连接信息

    在这里插入图片描述

    在这里插入图片描述

    注意事项: 如果使⽤ MySQL 是 5.x 之前的使⽤的是“com.mysql.jdbc.Driver”,如果是⼤于 5.x 使⽤的是“com.mysql.cj.jdbc.Driver”;mysql Driver默认是8.0,如果是8.0以上,则driver-class-name使用的是“com.mysql.cj.jdbc.Driver”,如果是8.0之前的,则使用的是“com.mysql.jdbc.Driver”;如果你创建的是新项目,并且spring boot版本号在2.6.9之前的,那么就只有“com.mysql.cj.jdbc.Driver”这一种写法。

    4.3.2 配置MyBatis的XML保存路径

    在这里插入图片描述

    5、使用MyBatis的操作模式操作数据库

    MyBatis模式如图所示:
    在这里插入图片描述

    5. 1 定义接口(普通的接口)

    package com.example.demo.mapper;
    
    import com.example.demo.model.UserInfo;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper // mybatis interface
    public interface UserMapper {
        // 根据用户id查询用户
        public UserInfo getUserById(Integer id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5. 2 创建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">
    <!--namespace 要设置是实现接口的具体包名加类名-->
    <mapper namespace="com.example.demo.mapper.UserMapper">
        <select id="getUserById" resultType="com.example.demo.model.UserInfo">
            select * from userinfo where id=${id}
        </select>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    5. 3 添加实体类

    先添加⽤户的实体类:

    package com.example.demo.model;
    
    import lombok.Data;
    
    /**
     * 普通实体类
     */
    @Data
    public class UserInfo {
        private int id;
        private String username;
        private String password;
        private String photo;
        private String createtime;
        private String updatetime;
        private int state;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5. 4 添加 Service

    服务层实现代码如下:

    package com.example.demo.service;
    
    import com.example.demo.mapper.UserMapper;
    import com.example.demo.model.UserInfo;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    @Service
    public class UserService {
        @Resource
        private UserMapper userMapper;
        public UserInfo getUserById(Integer id){
            return userMapper.getUserById(id);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5. 5 添加 Controller

    控制器层的实现代码如下:

    package com.example.demo.controller;
    
    import com.example.demo.model.UserInfo;
    import com.example.demo.service.UserService;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @RestController
    @RequestMapping("/user")
    public class UserController {
        @Resource
        private UserService userService;
        @RequestMapping("/getuserbyid")
        public UserInfo getUserById(Integer id){
            if(id==null)
                return null;
            return userService.getUserById(id);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    以上代码写完,整个 MyBatis 的查询功能就实现完了,测试结果如下:
    在这里插入图片描述
    至此我们发现,要验证功能的正确性过于繁琐了,那么如果我们只是想单纯的测试代码的正确性怎么办呢❓🤔这个时候就可以使用SpringBoot单元测试,请参考➡️SpringBoot单元测试

    5.6 添加日志文件

    在进行本地调试时,为了方便更加直观的查看跟数据库交互的具体情况,我们可以进行sql打印。

    # 开启 MyBatis SQL 打印
    logging:
      level:
        com:
          example:
            demo: debug
    mybatis:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    6、增、删、改操作

    接下来,我们来实现⼀下⽤户的增加、删除和修改的操作,对应使⽤ MyBatis 的标签如下:

    • < insert >标签:插⼊语句
    • < update >标签:修改语句
    • < delete >标签:删除语句

    6.1 增加用户操作

    1、在mapper(interface)里面添加方法声明
    在这里插入图片描述
    2、在xml 中实现添加业务
    在这里插入图片描述
    进行单元测试:
    在这里插入图片描述
    效果图如下:
    在这里插入图片描述

    MyBatis添加用户并返回自增ID

    1、添加方法声明
    在这里插入图片描述
    2、xml 实现方法
    在这里插入图片描述
    进行单元测试:
    在这里插入图片描述
    效果图如下:
    在这里插入图片描述
    在这里插入图片描述

    6.2 修改用户操作

    1、在interface里面添加修改方法的声明
    在这里插入图片描述

    2、在xml 中添加接口的实现标签和具体的执行sql
    在这里插入图片描述
    进行单元测试:
    在这里插入图片描述
    效果图如下:
    在这里插入图片描述
    在不需要数据的前提下,执行单元测试:(这是单元测试其中的一个好处:使用单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进行任何改变的情况下,测试功能
    在这里插入图片描述
    在这里插入图片描述

    6.3 删除用户操作

    1、在mapper(interface)里面添加删除的代码声明
    在这里插入图片描述
    2、在xml 中添加< delete >标签和删除的sql 编写
    在这里插入图片描述
    进行单元测试:
    在这里插入图片描述
    效果图如下:
    在这里插入图片描述

    7、查询操作

    7.1 单表查询

    7.1.1 参数占位符 #{} 和 ${}的区别⭐⭐⭐⭐

    使用 #{} 得到JDBC的代码如下(针对int类型的参数):
    在这里插入图片描述
    使用 ${} 得到JDBC的代码如下(针对int类型的参数):
    在这里插入图片描述
    使用 #{} 得到JDBC的代码如下(针对string类型的参数):
    在这里插入图片描述
    使用 ${} 得到JDBC的代码如下(针对string类型的参数):
    在直接替换的时候并没有给字符串的value值加上单引号
    在这里插入图片描述

    在这里插入图片描述

    #{} 和 $ {}的区别:

    1. 定义不同:#{} 预处理;$ {} 是直接替换。
    2. 使用不同: #{}适用于所有类型的参数匹配;但$ {}只适用数值类型。
    3. 安全性不同: #{}性能高,并且没有安全问题;但$ {}存在SQL注入的安全问题。

    7.1.2 ${}使用场景(优点)

    在这里插入图片描述
    当我们使用#{}时,效果图如下:
    在这里插入图片描述

    小结:当传递的是一个SQL关键字(SQL 命令)的时候,只能使用${}, 此时如果使用#{} 就会认为传递的为一个普通的值,而非SQL命令,所以执行就会报错。
    $ {} 注意事项:当不得不使用${}时,那么一定要在业务代码中,对传递的值进行安全效验。

    7.1.3 SQL 注入问题

    以登录功能来举例:
    在这里插入图片描述
    在这里插入图片描述
    进行测试:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    以上情况均符合我们的预期,用户名和密码都正确才能拿到用户信息,而有一方错误的话,用户信息则为null。但是,当我们把密码改成下面这个样子的时候,就超出我们的预期了
    SQL注入演示($()):
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    当我们使用$()的时候会发生SQL注入问题,那么当我们使用#()会不会出现这个问题呢❓🤔
    在这里插入图片描述
    其他不变,密码还是那个密码,看是否会发生SQL注入。效果图如下:
    在这里插入图片描述
    如上图所示,在密码错误的情况下,并没有拿到用户信息,也就是没有发生SQL注入问题😊😊😊

    7.1.4 like 查询

    使用#()时:
    在这里插入图片描述
    在这里插入图片描述
    进行测试:
    在这里插入图片描述
    效果图如下:
    在这里插入图片描述
    通过上图发现有报错,这是因为使用#(),它会使用 进行预处理,而 是string 类型的,所以在预处理的时候它会给你拼个单引号,而最终的结果就是:
    在这里插入图片描述
    那么既然使用#()不行,就使用$()试试:
    在这里插入图片描述
    测试结果如下:
    在这里插入图片描述

    上面的结果看起来没有问题,我们也成功的得到了用户信息,但是我们在之前说过一个问题,就是在使用$()时一定要对这个参数的有效性进行校验,上面我们在进行排序的时候进行校验,要么是升序要么是降序排序,是可以穷举的,但是这次根据名称进行模糊查询,需要校验的可能性太多了,因为你不知道用户会输入什么进行查询,所以在业务层的值无法穷举,那么也就导致了SQL注入的问题。

    使用#()报错,但使用$()在业务层的值又不能穷举,那么该如何处理这个问题呢❓🤔
    此时我们可以考虑使⽤ mysql 的内置函数 concat() 来处理
    在这里插入图片描述
    测试结果如下:
    在这里插入图片描述

    7.2 多表查询

    7.2.1 返回类型:resultType

    绝⼤数查询场景可以使⽤ resultType 进⾏返回,如下代码所示:

    <!--根据id查询用户-->
        <select id="getUserById" resultType="com.example.demo.model.UserInfo">
            select * from userinfo where id=${id}
        </select>
    
    • 1
    • 2
    • 3
    • 4

    它的优点是使⽤⽅便,直接定义到某个实体类即可。

    7.2.2 返回字典映射:resultMap

    resultMap 使⽤场景:

    • 字段名称和程序中的属性名不同的情况,可使⽤ resultMap 配置映射;
    • ⼀对⼀和⼀对多关系可以使⽤ resultMap 映射并查询数据。

    字段名和属性名不同的情况:
    在这里插入图片描述
    测试结果如下:
    在这里插入图片描述
    这个时候就可以使⽤ resultMap 了,resultMap 的使⽤如下:

    <resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
            <!--主键映射-->
            <id column="id" property="id"></id>
            <!--主普通属性映射-->
            <result column="username" property="name"></result>
        </resultMap>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    测试结果如下:
    在这里插入图片描述

    7.2.3 一对一查询

    ⼀对⼀映射要使用 标签,具体实现如下(⼀篇⽂章只对应⼀个作者):
    model层:

    package com.example.demo.model;
    
    import lombok.Data;
    
    @Data
    public class ArticleInfo {
        private int id;
        private String title;
        private String content;
        private String createtime;
        private String updatetime;
        private int uid;
        private int rcount;
        private int state;
        private UserInfo userInfo;// 多了一个外键对象属性
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    mapper层:

    package com.example.demo.mapper;
    
    import com.example.demo.model.ArticleInfo;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper    // 一定不要忽略此注解
    public  interface ArticleMapper {
        // 根据文章id查询文章
        public ArticleInfo getArticleById(Integer id);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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">
    <!--namespace 要设置是实现接口的具体包名加类名-->
    <mapper namespace="com.example.demo.mapper.ArticleMapper">
        <resultMap id="BaseMapper" type="com.example.demo.model.ArticleInfo">
            <id column="id" property="id"></id>
            <result column="title" property="title"></result>
            <result column="content" property="content"></result>
            <result column="createtime" property="createtime"></result>
            <result column="updatetime" property="updatetime"></result>
            <result column="uid" property="uid"></result>
            <result column="rcount" property="rcount"></result>
            <result column="state" property="state"></result>
            <association property="userInfo" resultMap="com.example.demo.mapper.UserMapper.BaseMap"
            columnPrefix="u_"></association>
        </resultMap>
        <select id="getArticleById" resultMap="BaseMapper">
            select a.*,u.id u_id,u.username u_username,u.password u_password,u.photo u_photo,u.createtime u_createtime,u.updatetime u_updatetime,u.state u_state
            from articleinfo a left join userinfo u on a.uid=u.id where a.id=#{id}
        </select>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    单元测试代码:

    package com.example.demo.mapper;
    
    import com.example.demo.model.ArticleInfo;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import javax.annotation.Resource;
    
    import static org.junit.jupiter.api.Assertions.*;
    @Slf4j
    @SpringBootTest
    class ArticleMapperTest {
    
        @Resource
        private ArticleMapper articleMapper;
        @Test
        void getArticleById() {
            ArticleInfo articleInfo= articleMapper.getArticleById(1);
            log.info("文章详情:"+articleInfo);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    结果图如下:
    在这里插入图片描述
    以上使⽤ 标签,表示⼀对⼀的结果映射:

    • property 属性:指定 Article 中对应的属性,即⽤户。
    • resultMap 属性:指定关联的结果集映射,将基于该映射配置来组织⽤户数据。
    • columnPrefix 属性:绑定⼀对⼀对象时,是通过columnPrefix+association.resultMap.column 来映射结果集字段。association.resultMap.column是指 标签中 resultMap属性,对应的结果集映射中,column字段。

    注意事项: column不能省略
    在这里插入图片描述

    7.2.4 一对多查询

    ⼀对多需要使⽤ 标签,⽤法和 相同,如下所示:

    model层:

    package com.example.demo.model;
    
    import lombok.Data;
    
    import java.util.List;
    
    /**
     * 普通实体类
     */
    @Data
    public class UserInfo {
        private Integer id;
        private String name;
        private String password;
        private String photo;
        private String createtime;
        private String updatetime;
        private int state;
        private List<ArticleInfo> artlist;// 增加了文章这个外键对象属性
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    mapper层:

    // 根据用户id查询用户及用户发表的所有文章
        public UserInfo getUserAndArticleByUid(@Param("uid") Integer uid);
    
    • 1
    • 2

    XML层(实现上面的接口):

    <resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
            <!--主键映射-->
            <id column="id" property="id"></id>
            <!--主普通属性映射-->
            <result column="username" property="name"></result>
            <result column="password" property="password"></result>
            <result column="photo" property="photo"></result>
            <result column="createtime" property="createtime"></result>
            <result column="updatetime" property="updatetime"></result>
            <result column="state" property="state"></result>
            <collection property="artlist" resultMap="com.example.demo.mapper.ArticleMapper.BaseMapper"
            columnPrefix="a_"></collection>
        </resultMap>
        <select id="getUserAndArticleByUid" resultMap="BaseMap">
            select u.*,a.id a_id,a.title a_title,a.content a_content, a.createtime a_createtime,
            a.updatetime a_updatetime,a.uid a_uid,a.rcount a_rcount,a.state a_state
            from userinfo u left join articleinfo a on u.id=a.uid where u.id=#{uid}
        </select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    单元测试代码:

     @Test
        void getUserAndArticleByUid() {
            UserInfo userInfo= userMapper.getUserAndArticleByUid(1);
            log.info("用户详情:"+userInfo);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果图如下:
    在这里插入图片描述

    8、动态 SQL

    动态 sql 是Mybatis的强⼤特性之⼀,能够完成不同条件下不同的 sql 拼接。
    可以参考官⽅⽂档:Mybatis动态sql

    8.1 if 标签

    在注册⽤户的时候,可能会有这样⼀个问题,注册分为两种字段:必填字段和非必填字段,那如果在添加⽤户的时候有不确定的字段传⼊,程序应该如何实现呢❓🤔 这个时候就需要使⽤动态标签 来判断了:
    判断一个参数是否有值的,如果没有值,那么就会隐藏if中的sql
    语法如下:
    在这里插入图片描述
    mapper层:

    // 添加用户,添加用户时photo是非必传参数
        public int add2(UserInfo userInfo);
    
    • 1
    • 2

    XML层(实现上面的接口):

    <!-- 添加用户,添加用户时photo是非必传参数-->
        <insert id="add2">
            insert into userinfo(username,password
            <if test="photo!=null">
                ,photo
            </if>
            ) values(#{username},#{password}
            <if test="photo!=null">
                ,#{photo}
            </if>
            )
        </insert>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    测试代码(传photo值时):

    @Test
        void add2() {
            UserInfo userInfo=new UserInfo();
            userInfo.setUsername("潘潘");
            userInfo.setPassword("123");
            userInfo.setPhoto("jiao.png");
            int result= userMapper.add2(userInfo);
            log.info("添加用户的结果:"+result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    测试代码(不传photo值时):

     @Test
        void add2() {
            UserInfo userInfo=new UserInfo();
            userInfo.setUsername("一一");
            userInfo.setPassword("123456");
            //userInfo.setPhoto("jiao.png");
            int result= userMapper.add2(userInfo);
            log.info("添加用户的结果:"+result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    数据库中相对应的数据:
    在这里插入图片描述

    8.2 trim 标签

    最主要的作用:去除SQL语句前后多余的某个字符的
    之前的插入⽤户功能,只是有⼀个 photo字段可能是选填项,如果有多个字段,⼀般考虑使⽤标签结合标签,对多个字段都采取动态⽣成的方式。
    标签中有如下属性:

    • prefix:表示整个语句块,以prefix的值作为前缀
    • suffix:表示整个语句块,以suffix的值作为后缀
    • prefixOverrides:表示整个语句块要去除掉的前缀
    • suffixOverrides:表示整个语句块要去除掉的后缀

    语法如下:
    在这里插入图片描述
    由于上面为了演示字段名称和程序中的属性名不同的情况,可使⽤ resultMap 配置映射的情况,把UserInfo类中的username改成了name,为了下面编写方便,现又重新更改为username
    mapper层:

    // 添加用户,其中username,password.photo 都是非必传参数,但至少会传递一个参数
        public int add3(UserInfo userInfo);
    
    • 1
    • 2

    XML层(实现上面的接口):

    <insert id="add3">
            insert into userinfo
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="username!=null">
                    username,
                </if>
                <if test="password!=null">
                    password,
                </if>
                <if test="photo!=null">
                    photo
                </if>
            </trim>
            values
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="username!=null">
                    #{username},
                </if>
                <if test="password!=null">
                    #{password},
                </if>
                <if test="photo!=null">
                    #{photo}
                </if>
            </trim>
        </insert>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    测试代码:

    @Test
        void add3() {
            UserInfo userInfo=new UserInfo();
            userInfo.setUsername("穗穗");
            userInfo.setPassword("145");
            userInfo.setPhoto("apan.png");
            int result= userMapper.add3(userInfo);
            log.info("添加用户的结果:"+result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    8.3 where 标签

    主要作用:就是实现查询中的 where sql 替换的,它可以实现如果没有任何的查询条件,那么它可以隐藏查询中的 where sql,但是如果存在查询条件,那么会生成 where 的 sql 查询,并且使用 where 标签可以自动的去除最前面一个 and 字符
    首先由于设置的只能返回一个,所以数据库中只保留了一条数据:
    在这里插入图片描述
    mapper层:

    // 根据用户id查询用户
        public UserInfo getUserById(@Param("id") Integer id);
    
    • 1
    • 2

    XML层(实现上面的接口):

    <select id="getUserById" resultMap="BaseMap">
            select * from userinfo
            <where>
                <if test="id!=null">
                    id=#{id}
                </if>
            </where>
        </select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试代码:

    @Test
        void getUserById() {
            UserInfo userInfo=userMapper.getUserById(null);
            log.info("用户信息"+userInfo);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    当我们的条件参数有多个时,要用and来连接,那么加上and会是怎样呢❓ 🤔
    在这里插入图片描述
    测试代码:
    在这里插入图片描述
    在这里插入图片描述
    以上标签也可以使⽤ 替换。

    8.4 set 标签

    作用: 在进行修改操作时,配合 if 来处理非必传输的,它的特点就是会自动去除最后一个英文逗号
    语法如下:
    在这里插入图片描述
    mapper层:

    public int update2(UserInfo userInfo);
    
    • 1

    XML层(实现上面的接口):

    <update id="update2">
            update userinfo
            <set>
                <if test="username!=null">
                    username=#{username},
                </if>
                <if test="password!=null">
                    password=#{password},
                </if>
                <if test="photo!=null">
                    photo=#{photo}
                </if>
            </set>
            where id=#{id}
        </update>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    测试代码:

    @Test
        void update2() {
            UserInfo userInfo=new UserInfo();
            userInfo.setId(1);
            userInfo.setUsername("潘潘");
            int result= userMapper.update2(userInfo);
            log.info("update2 修改的结果为:"+result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    在这里插入图片描述
    当最后没有英文逗号时:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    以上标签也可以使⽤ 替换。

    8.5 foreach 标签

    作用: 主要就是对集合进行循环的

    标签有如下属性:

    • collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
    • item:遍历时的每⼀个对象
    • open:语句块开头的字符串
    • close:语句块结束的字符串
    • separator:每次遍历之间间隔的字符串

    示例:根据多个⽂章 id 来删除⽂章数据
    mapper层:

    int delIds(List<Integer> list);
    
    • 1

    XML层(实现上面的接口):

    <delete id="delIds">
            delete from userinfo where id in
            <foreach collection="list" open="(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </delete>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    为方便观察,我们可以现在数据库中加几条数据:
    在这里插入图片描述
    测试代码:

    @Test
        void delIds() {
            List<Integer>list=new ArrayList<>();
            list.add(11);
            list.add(12);
            list.add(12);
            int result= userMapper.delIds(list);
            log.info("批量删除的结果:"+result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

  • 相关阅读:
    Cookie 与 Session的区别
    基于片段的分子生成网络 (FLAG)使用方法及案例测评
    Flutter高仿微信-第43篇-群聊列表
    SpringCloud Sentinel 使用
    初始JDBC 编程
    让我十二点催她睡觉,我用 Python 轻松解决
    盲人盲杖:科技革新,助力视障人士独立出行
    如何在Ubuntu 18.04上安装Go并设置本地编程环境
    我从自动化测试转为测试开发,资深测试总结测试开发技术栈,提升之路......
    ApDBUtils引出、土方法完成封装
  • 原文地址:https://blog.csdn.net/m0_46468731/article/details/125956152