MyBatis

MyBatis

  • MyBatis 是什么
  • 使用 MyBatis
    • 创建 MyBatis 依赖
    • 配置数据库连接字符串
    • 配置 MyBatis 保存的 xml 目录
    • MyBatis 的操作模式
    • 第一个查询
  • MyBatis 执行过程
  • Spring Boot 单元测试
    • 什么是单元测试
    • 进行单元测试的步骤
      • 添加测试框架
      • 先生成单元测试的类
      • 配置单元测试的类
  • 增删改查
    • 修改方法
    • 事务回滚
    • 删除方法
    • 添加数据
      • 返回受影响的行数
      • 返回自增 id
  • ${} 和 #{} 的区别
    • #{}
    • ${}
    • 区别
    • SQL 注入
  • resultMap 和 resultType
    • resultType
    • resultMap
  • 多表查询
    • 一对一查询
      • 通过 resultMap 来完成多表查询:
    • 一对多查询
  • 动态 SQL
    • if 标签
    • trim 标签
    • where 标签
    • set 标签
    • foreach 标签

MyBatis 是什么

MyBatis 是一个持久层框架,也就是对整个框架里面的数据进行持久化的,是一个 ORM 框架(对象关系映射)就是连接项目和数据库的,支持自定义 SQL,存储过程 以及 高级映射。几乎去除了所有的 JDBC 代码 和 设置参数和获取结果集的⼯作。更简单的就是,MyBatis 可以更简单的完成项目和数据库的交互。

  1. 支持自定义 SQL:就是 增删改查 都可以写,也就是说明 MyBatis 是很灵活的。

  2. 存储过程:
    a)我们之前写的 SQL,不管是多复杂的 SQL,都是一行写完,因为没有复杂的业务场景。
    b)像 for 循环,判断这些都没有。但是存储是 SQL 当中的方法,由一大堆 SQL 构成。
    c)这个组成里面,是由循环,判断,分支,变量传递的。就叫做存储过程,也就是 SQL 方法化。
    d)也就是说 SQL 方法化的过程所产生的东西,就叫做存储的过程。

  3. 高级映射:除了可以实现 数据表 和 程序里面的对象 映射之外,还可以实现 一对一的多表映射 和 一对多的映射。
    a)比如说 CSDN,每一篇作者,都有它的作者,文章和作者的关系,就是一对一的关系。
    b)对作者来说,可以发很多文章,作者和文章的关系就是一对多的关系。

  4. 我们主要通过 XML 来实现 SQL 语句,因为如果用 注解的话,会导致很难写。

使用 MyBatis

使用 JDBC 的话,使用起来很麻烦,很繁琐。使用 MyBatis 的话,可以直接连接,使用方法很简单。我们创建一个数据库:

-- 创建数据库
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);

创建 MyBatis 依赖

在创建项目的时候,就添加 MyBatis 相关依赖:
MyBatis_第1张图片
将这两个依赖全部导入就可以了,因为 MyBatis 是操作数据库的,所以也要导入 MySQL 依赖。

如果是之前就有的项目,那么在 pom.xml 里面通过 Edit Starters 导入就好了
MyBatis_第2张图片

配置数据库连接字符串

在启动项目之前,一定要配置数据库,不然会报错:
MyBatis_第3张图片
在配置文件当中配置数据库信息:

#配置数据库环境
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8
    username: root
    password: sjp151
    driver-class-name: com.mysql.cj.jdbc.Driver

然后启动项目就不会报错了。

配置 MyBatis 保存的 xml 目录

因为 注解方式不好用,所以我们还是用 xml ,放在 resource 目录下,然后创建 mybatis 文件夹,然后放进去,也是为了提升可读性。我们创建一个 UserMapper :
MyBatis_第4张图片
然后在 配置文件 当中指定 mapper 路径:

#配置 mybatis xml 保存路径
mybatis:
  mapper-locations: classpath:mybatis/**Mapper.xml

MyBatis 的操作模式

一个项目的流程如下图:
MyBatis_第5张图片
MyBatis 的操作模式,有两个部分

  1. Interface(方法定义,接口)
  2. xxx.xml

在 Interface 当中加一个 @Mapper 注解,就表示当前的这个 mybatis 类,就 实现 了对象的映射。

第一个查询

根据用户 id 来查询用户信息的操作,我们之前的数据库操作,就已经在 userinfo 表中插入了一个数据:
在这里插入图片描述
然后在 userinfo 当中写一样的属性

@Data
public class UserInfo {
    private int id;
    private String name;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;artList;
}

一定要保证和数据库当中表的内容一模一样,不然是不能操作数据库的。

然后在 Mapper 当中就可以写数据了,在 Mapper 当中的数据,一定要写 @Mapper 注解,起名的时候,和 Mapper 中的方法名称一样,便于可读性提升,创建 xml 的时候,配置文件必须要有下面这些配置文件,直接复制就行了:

#mapper的 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 写的是包名的路径:
MyBatis_第6张图片
查询的时候,也是 select 标签,然后 id 表明的是哪个 mapper,resultType 是指返回类型,也是要有 包名和类型 。UserMapper 中代码如下:

@Mapper
public interface UserMapper {
    public UserInfo getUserById(@Param("id") Integer id);
}

加 @param 注解是为了表示 xml 当中,用 id 可以获取到,就是通过 $ 符和 # 来获取到。

xml 如下:

<select id="getUserById" resultType="com.example.mybatisdemo.model.UserInfo">
    select * from userinfo where id=${id}
select>

在 Service 当中获取服务:

@Service
public class UserService {

    @Resource
    private UserMapper userMapper;
    public UserInfo getUserById(Integer id) {
        return userMapper.getUserById(id);
    }
}

然后在 Controller 当中进行检验:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getuserbyid")
    public UserInfo getUserById(Integer id) {
        if (id == null) {
            return null;
        }
        return userService.getUserById(id);
    }
}

然后直接运行就好了,使用 id 为 1:
MyBatis_第7张图片

MyBatis 执行过程

MyBatis_第8张图片
前端的程序,通过 Ajax 访问 服务器,发过来的信息,先到 Controller 层,对参数校验之后,然后把数据发给 Service 层,Service 对传输过来的信息,判断需要调用多少个接口,以及调用接口的先后顺序。

Mapper 是单一的,不会有太复杂的业务逻辑。如果 Mapper 中的业务过于复杂,就会导致业务耦合。也就是说 MyBatis 是基于 JDBC 实现的,所以在执行的时候,还是会生成 相应的 jdbc 代码 和 sql 语句,如下:

MyBatis_第9张图片

Spring Boot 单元测试

什么是单元测试

  1. 单元测试是对程序中的 最小单元 进行检查和验证的过程就叫做单元测试。
  2. 开发者通过一小段代码,来检验代码的一个很小的功能是否正确,是否完善。
  3. 单元测试的好处:
    a)单元测试不需要启动 Tomcat
    b)如果中途改动了代码,在项目打包的时候会发现错误,因为打包之前,所有单元测试都必须通过,然后才能打包成功。
    c)如果不使用单元测试的话,会导致访问本地数据库,也就是会 “污染” 本地数据库。

进行单元测试的步骤

添加测试框架

也就是添加依赖,不过在我们现在使用的 Spring Boot 版本当中,已经自动加了以下依赖了:
MyBatis_第10张图片
所以我们就不需要重新添加了。

先生成单元测试的类

只需要在需要单元测试的类当中,一键生成 test 就好了:
MyBatis_第11张图片
点击之后:
MyBatis_第12张图片
生成之后如下:
MyBatis_第13张图片

配置单元测试的类

配置类的时候,要给类加上 @SpringBootTest 注解:
MyBatis_第14张图片

然后添加单元测试的业务代码,通过 @Resource 或者 @Autowired 注解来引入:

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void getUserById() {
        UserInfo userInfo = userMapper.getUserById(1);
        System.out.println(userInfo);
    }
}

然后直接点击旁边的 运行 按钮就好了:
MyBatis_第15张图片
运行结果如下:
MyBatis_第16张图片
绿色的对勾表示单元测试成功,也就是通过了单元测试。

也可以通过断言来表示单元测试是否成功,常用的断言如下:
在这里插入图片描述
断言代码如下:

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void getUserById() {
        UserInfo userInfo = userMapper.getUserById(1);
        Assertions.assertNotNull(userInfo);
    }
}

运行结果如下,绿色表示通过:
MyBatis_第17张图片

增删改查

查询我们之前已经讲过了,还剩 增删改。

修改方法

代码如下:

@Mapper
public interface UserMapper {
    public int update(@Param("id") Integer id, @Param("username") String username);
}

xml 的 SQL 如下:
MyBatis_第18张图片
然后继续使用 单元测试 来测试就好了:

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void update() {
        int result = userMapper.update(2,"wangwu");
        Assertions.assertEquals(1,result);
    }
}

测试结果如图:
MyBatis_第19张图片

事务回滚

就是在方法上面加一个 @Transactional 注解,就表示方法执行完之后,回滚数据库,数据库原始数据如下:
MyBatis_第20张图片
测试代码:

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void update() {
        int result = userMapper.update(2,"zhaoliu");
        Assertions.assertEquals(1,result);
    }
}

单元测试是通过的,也就是受影响的行数时 1,结果:
MyBatis_第21张图片
数据库并没有被污染,如果不加的话,数据库就被修改(污染)了。

删除方法

方法如下:
MyBatis_第22张图片
xml 的 SQL:
MyBatis_第23张图片

测试代码:

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void delete() {
        int result = userMapper.delete(2);
        System.out.println("受影响的行数:" + result);
    }
}

运行结果如下:
MyBatis_第24张图片

添加数据

返回受影响的行数

先添加 “返回受影响的行数” 的添加方法:
MyBatis_第25张图片
xml 当中 SQL :
在这里插入图片描述
测试数据:
MyBatis_第26张图片

测试结果如下:
MyBatis_第27张图片

返回自增 id

返回自增 id,也就是需要多设置 属性名 就好了:

<insert id="addGetId" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    insert into userinfo(username,password,photo) values(#{username},#{password},#{photo});
insert>

也就是返回 id,测试代码如下:
MyBatis_第28张图片
运行结果如下:
MyBatis_第29张图片

${} 和 #{} 的区别

#{}

先使用 # 来完成测试,针对 Integer 类型的参数:

<select id="getUserById" resultType="com.example.mybatisdemo.model.UserInfo">
    select * from userinfo where id=#{id}
select>

测试生成的 SQL 如下:
MyBatis_第30张图片
用 # 就是先生成一个占位符,然后再去给 占位符 设置数据。占位符就是预处理(预查询)。可以很好的提升查询效率,也会更加安全。

根据用户名字来查询

<select id="getUserByFullName" resultType="com.example.mybatisdemo.model.UserInfo">
    select * from userinfo where username=#{username};
select>

单元测试:
MyBatis_第31张图片
运行结果如下:
MyBatis_第32张图片

${}

现在用 ${} ,针对 Integer 类型的参数,测试生成的 SQL 如下:
MyBatis_第33张图片
SQL 就没有 ?了,而是直接被替换为参数。就是即时查询。

通过用户名字来查询

<select id="getUserByFullName" resultType="com.example.mybatisdemo.model.UserInfo">
    select * from userinfo where username=${username};
select>

换成 ${} 符号之后,来查询,结果报错:
MyBatis_第34张图片
发现 lisi 并没有被引号引起来。因为 ${} 是针对 String 类型的,因为没有 引号,所以导致 SQL 出错。

区别

  1. #{} 是预处理,${} 是直接替换。
  2. #{} 适用于所有类型的参数匹配,${} 指适用于数值类型。
  3. #{} 性能高,并且没有安全问题。但 ${} 存在 SQL 注入的问题。
  4. 如果在 构造SQL语句 的时候,如果替换的参数,是 SQL 关键字,使用 ${} 更好,比如说排序的时候,直接用 desc,asc 这样去替换。如果是用于 字段 的话,需要获取到参数类型的信息,使用 #{} 更好。

SQL 注入

在我们登录的时候,要传两个参数,就是用户名和密码,只有两个都正确的时候,才能登陆成功。xml 如下:

在这里插入图片描述

测试代码:
MyBatis_第35张图片
运行结果如下,成功通过测试案例:
MyBatis_第36张图片
如果使用 SQL 注入的话,测试代码如下

@Test
void login() {
    String username = "lisi";
    String password = "' or 1='1";
    UserInfo userInfo = userMapper.login(username, password);
    log.info("用户信息" + userInfo);
}

也就是在判断的时候,是通过 or 来判断的,后面的 1=1,等式恒为真,所以会成功运行:
MyBatis_第37张图片
密码是不正确的,但是却查询出来了信息,相当于没有取款密码,但是把钱取走了。换为 #{} 就不存在安全漏洞了:
MyBatis_第38张图片

  • List item

模糊查询
通过 like 来进行模糊查询,方法如下:

public List<UserInfo> getListByName(@Param("username") String username);

xml 代码如下:
MyBatis_第39张图片

测试代码如下:

@Test
void getListByName() {
    String username = "a";
    List<UserInfo> list = userMapper.getListByName(username);
    log.info("用户列表:" + list);
}

成功通过测试:
MyBatis_第40张图片
但是 ${} 一定会有 sql 注入的问题,所以我们就可以通过 concat 来拼接,然后通过 #{} 来完成模糊查询:

<select id="getListByName" resultType="com.example.mybatisdemo.model.UserInfo">
    select * from userinfo where username like concat('%',#{username},'%');
select>

查询结果如下:
MyBatis_第41张图片

resultMap 和 resultType

resultType

如果字段名和数据库的字段名不一致的时候,就会导致查询出来的字段为 null,我们把 userinfo 的 username 换成 name,然后查询:
MyBatis_第42张图片

运行结果为 null:
MyBatis_第43张图片
所以就可以通过 resultMap 来解决这样的问题。

resultMap

resultMap 有两个参数,有 id 和映射的内容 type :

<resultMap id="BaseMap" type="com.example.mybatisdemo.model.UserInfo">resultMap>

然后在参数挡住设置 id 和 result 就好,id 是主键映射,result 是普通属性映射。

  1. id 里面一定要有的字段:column,就是字段名是啥。property 就是映射的属性是什么,这两个属性是必须有的。
  2. result 也有两个字段:column 是数据库对应的数据名,property 是项目中的属性名称。

但是这样写的话,在单表查询当中是没问题的,然后在 SQL 语句当中就可以使用 resultMap :

<resultMap id="BaseMap" type="com.example.mybatisdemo.model.UserInfo">
    <id column="id" property="id">id>
    <result column="username" property="name">result>
resultMap>

sql 如下:

<select id="getListByName" resultMap="BaseMap">
    select * from userinfo where username like concat('%',#{username},'%');
select>

测试结果如下,现在就可以获取到名字了:MyBatis_第44张图片

多表查询

一对一查询

多表查询当中,mybatis 是支持 一对一 和一对多 查询的。针对文章信息,写一个实体类:

@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;
}

通过 UserInfo 来和 user 表进行关联。建一个接口:

@Mapper
public interface ArticleMapper {

    public ArticleInfo getArticleById (@Param("id") Integer id);
}

XML 中的 SQL 代码如下:

<select id="getArticleById" resultType="com.example.mybatisdemo.model.ArticleInfo">
    select a.*,u.id u_id,u.username u_username,u.password u_password from articleinfo a left join userinfo u on a.uid=u.id
    where a.id=#{id}
select>

测试代码:

@SpringBootTest
@Slf4j
class ArticleMapperTest {

    @Resource
    private ArticleMapper articleMapper;

    @Test
    void getArticleById() {
        ArticleInfo articleInfo = articleMapper.getArticleById(1);
        log.info("文章详情" + articleInfo);
    }
}

运行结果如下:
MyBatis_第45张图片
成功通过测试,但是 userinfo 的结果为 null:
MyBatis_第46张图片
这里的 resultType 没有查询出来,那么就通过 resultMap 来完成查询。

通过 resultMap 来完成多表查询:

在使用的时候,也要把其它属性映射。但是属性里面还有 userinfo 所以通过 association 来连接,association 就是用来连接表的。因为两张表的信息都要查到,所以另外一张表的 xml 当中也应该写出所有配置。

ArticleMapper 的 xml :

<resultMap id="BaseMap" type="com.example.mybatisdemo.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.mybatisdemo.mapper.UserMapper1.BaseMap">
    association>
resultMap>

UserMapper 的 XML :

<resultMap id="BaseMap" type="com.example.mybatisdemo.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>
resultMap>

这里 association 里面的 resultMap 也要保证 userInfo 里面有,因为是通过这个来连接的。

XML 中的 SQL 语句:

<select id="getArticleById" resultMap="BaseMap">
    select a.*,u.* from articleinfo a left join userinfo u on a.uid=u.id
    where a.id=#{id}
select>

查询结果如下,现在就能查到 userInfo 的信息了:
MyBatis_第47张图片
但是如果字段一样的话,就会导致值的覆盖,也就是不同表的值是一样的,为了解决这种问题,就可以通过设置前缀来解决,设置前缀就相当于是把表的字段重命名了。通过 columnPrefix 来设置前缀:

<association property="userInfo"
      resultMap="com.example.mybatisdemo.mapper.UserMapper.BaseMap"
      columnPrefix="u_">
association>

这样的话,就不会出现数据覆盖问题了。

一对多查询

查询一个作者的多篇文章,通过 List 来返回:

@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;
    //作为文章列表
    private List<ArticleInfo> artList;
}

Mapper 代码:

public UserInfo getUserAndArticleByUid(@Param("uid") Integer uid);

XML 当中,因为要用到 ArticleInfo 表,所以也要使用多表查询,通过 collection 来设置。通过 property 来约束数据类型,和 resultMap 来指定 BaseMap,然后通过 columnPrefix 来对字段重命名:

<resultMap id="BaseMap" type="com.example.mybatisdemo.model.UserInfo">
    <id column="id" property="id">id>
    <result column="username" property="username">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>

    <collection property="artList"
                resultMap="com.example.mybatisdemo.mapper.ArticleMapper.BaseMap"
                columnPrefix="a_">
    collection>
resultMap>

XML 当中的 SQL:

<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 from userinfo u left join
           articleinfo a on u.id=a.uid where u.id=#{uid}
select>

测试结果如下,信息都能查到:
MyBatis_第48张图片

动态 SQL

动态 sql 是 MyBatis 的强⼤特性之⼀,能够完成不同条件下不同的 sql 拼接。也就是有的参数是非必须传的,所以就可以通过 动态 sql 来拼接。

if 标签

判断一个参数是否是有值的,如果没值,那么就会隐藏 if 中的 SQL,也就是针对一些非必传参数,如果没有参数的话,就不执行 SQL,语法如下:

<if test="username!=null">
	username=#{username}
if>

表达式的意思是:username 的参数是否为空。如果结果是 true,那么拼接的 sql 就会加上 username=#{username} 。比如说对于非必传参数,就可以直接用 if 来选中了:

<insert id="add2">
    insert into userinfo(username,password
    <if test="photo!=null">
        ,photo
    </if>
    ) values (#{name},#{password}
    <if test="photo!=null">
        ,#{photo}
    </if>
    )
</insert>

这样就完成了 sql 的拼接。然后通过测试:
MyBatis_第49张图片

测试结果如下:
MyBatis_第50张图片
当 photo 不传值的时候:

@Test
void add2() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("zhaoliu");
    userInfo.setPassword("123");
    int result = userMapper.add2(userInfo);
    log.info("添加用户结果:" + result);
}

测试结果如下:
MyBatis_第51张图片
这里就没有 photo 了,数据库如下:
MyBatis_第52张图片

trim 标签

最主要的作用,去除 SQL 语句前后多余的某个字符。一共有四个属性:

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

语法如下:

<trim prefix="(" suffix=")" prefixOverrides="," suffixOverrides=",">
	if< test="xxx">
	...
	if>
	...
trim>

再使用一个 add 方法来添加,SQL 如下:

<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="name!=null">
            #{username},
        if>
        <if test="password!=null">
            #{password},
        if>
        <if test="photo!=null">
            #{photo}
        if>
    trim>
insert>

然后进行测试,运行结果如下:
MyBatis_第53张图片
我们的 sql 语句当中,password 后面是有 逗号 的,但是这里把 逗号 省略了。

where 标签

主要作用是实现查询中的 where sql 替换的,它可以实现如果没有任何的查询条件,那么它可以隐藏查询中的 where sql,但如果存在 查询条件,那么会生成 where 的 sql 查询,并且使用 where 标签可以自动去除前面的 and 字符

通过 id 来查询,SQL 如下:

<select id="getUserById" resultMap="BaseMap">
    select * from userinfo
    <where>
        <if test="id!=null">
            id=#{id}
        if>
    where>
select>

测试结果如下:
在这里插入图片描述
刚刚好查询,如果不传 id 的话:
MyBatis_第54张图片
运行结果如下,就没有生成 SQL:
MyBatis_第55张图片

set 标签

根据传⼊的⽤户对象属性来更新⽤户数据,可以使⽤标签来指定动态内容。UserMapper 接⼝中修改⽤户⽅法:根据传⼊的⽤户 id 属性,修改其他不为 null 的属性。set 标签,其实和 where 是一样的,都是替代 SQL 中的关键字。set 是 update 操作所需要使用的关键字。也是和 if 标签 配合使用的,也可以去掉最后一个符号。语法如下:

update table
	<set>
		<if test="xxx">
			...
		if>
		...
	set>
where ...

更新方法:

public int update(UserInfo userInfo);

SQL 如下:

<update id="update">
    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>

数据库原有的数据如下:
MyBatis_第56张图片
测试代码如下:

@Test
void update() {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(1);
    userInfo.setUsername("bbb");
    int result = userMapper.update(userInfo);
    log.info("update 的修改结果:" + result);
}

更新之后数据库如下:
MyBatis_第57张图片
也说明 set 会自动去除最后一个逗号。set 标签也可以用 trim 来实现。

foreach 标签

对集合进行遍历时可以使用该标签,foreach 标签有这些属性:

  1. collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
  2. item:遍历时的每⼀个对象(集合中的元素)
  3. open:语句块开头的字符串(类似 trim的 prefix)
  4. close:语句块结束的字符串(类似 trim的 close)
  5. separator:每次遍历之间间隔的字符串(间隔符)

foreach 标签是 trim 所不能替代的。主要用在数据库当中批量删除数据。先在数据库当中插入数据,数据如下:
MyBatis_第58张图片
XML 当中的 SQL 如下:

<delete id="deleteId">
    delete from userInfo where id in 
    <foreach collection="ids" open="(" close=")" item="id" separator=",">
        #{id}
    foreach>
delete>

测试代码如下:

@Test
void deleteId() {
    List<Integer> list = new ArrayList<>();
    list.add(6);
    list.add(7);
    list.add(8);
    int result = userMapper.deleteId(list);
    log.info("批量删除的结果:" + result);
}

运行结果如下:
MyBatis_第59张图片
数据库当中的信息:
MyBatis_第60张图片
成功完成了删除内容。

你可能感兴趣的:(JavaEE,mybatis,mysql,java,Spring,Boot,数据库)