目录
前言
在前一篇中,我们已经讲解了Mybatis的基本使用,那么本篇我们就进一步来了解Mybatis的功能。
本篇我们主要讲的是Mybatis中的动态SQL,在前面我们的学习中,我们编写的SQL语句基本都是固定的,每个参数都需要传递,而通过动态SQL,它能根据我们所给的参数,动态地选择参数,不需要每个都传递。
想了解更多关于动态SQL的可以看下面的网址:
MyBatis 动态SQL与数据准备http://www.mybatis.cn/mybatis/1942.html
预备工作
首先我们需要配置好项目,在pom.xml配置文件中导入mybatis依赖。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.4</version>
<scope>test</scope>
</dependency>
由于后序基本是用XML的形式来讲解,所以这里我们需要在application.yml文件中添加相应的配置。
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
mapper-locations: classpath:mapper/**Mapper.xml
后序以userinfo为操作对象,这里我们需要添加对应的类。
package com.example.demo.Model;
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private Integer id;
private String userName;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
创建对应的数据表:
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info(
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR(256) NOT NULL,
`password` VARCHAR(256) NOT NULL,
`age` INT NOT NULL,
`gender` INT NOT NULL,
`phone` VARCHAR(256) NOT NULL,
`delete_flag` INT DEFAULT 1 COMMENT '0-删除 1-存在',
`create_time` DATETIME DEFAULT NOW(),
`update_time` DATETIME DEFAULT NOW() ON UPDATE NOW()
)ENGINE = INNODB DEFAULT CHARSET=utf8mb4;
在我们的mapper.xml文件中编写SQL语句
其中的namespace是我们使用了MyBatis框架操作数据库的类的全限定类名称。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserInfoXMLMapper">
插入操作
我们有时候在注册账号的时候,或许会遇到要求我们输入自己的相关信息,其中这些信息包括:个人姓名、身份证号码等等,当然也存在着一些非必填的数据,那么对于这样的情况,我们在后端插入语句应该怎么写呢?
if
这就需要用到我们的<if>标签了,<if>标签能够帮助我们判断哪些字段是不为空的,再拼接到SQL语句中。
public Integer insert(UserInfo userInfo);
<insert id="insert">
insert into user_info(
<if test="userName!= null">
username,
</if>
<if test="password!=null">
password,
</if>
<if test="age!=null">
age,
</if>
<if test="gender!=null">
gender,
</if>
<if test="phone!=null">
phone,
</if>
)values(
<if test="userName!= null">
#{userName},
</if>
<if test="password!=null">
#{password},
</if>
<if test="age!=null">
#{age},
</if>
<if test="gender!=null">
#{gender},
</if>
<if test="phone!=null">
#{phone},
</if>
)
</insert>
进行单元测试:
@Test
void insert() {
UserInfo userInfo = new UserInfo();
userInfo.setUserName("666");
userInfo.setPassword("123456");
userInfo.setAge(18);
userInfo.setGender(1);
userInfoXMLMapper.insert(userInfo);
//{} 表示占位符 , 后面的参数会依次替换占位符
log.info("获取到的id为:{}", userInfo.getId());
}
当运行起来后,代码报错,是因为在上面中,我们给gender设置了为动态,且在传参的时候目传递,这就会导致在拼接SQL语句的时候,两个括号的结尾一定是逗号,这样的SQL语句是执行了不了的。
那么有没有什么办法去掉这两个逗号呢?
这里就需要用到我们的 <trim>标签了。
trim
<trim>标签能够帮助我们处理在SQL语句中多余的关键字、逗号等,也可以添加特定的前缀以及后缀。
<trim>标签有以下属性:
- prefix:表示整个语句块,以prefix的值作为前缀;
- suffix:表示整个语句块,以suffix的值作为后缀;
- prefixOverrides:表示整个语句块要去除的前缀;
- suffixOverrides:表示整个语句块要去除的后缀。
那么我们就可以对上面的SQL语句进行修改:
<insert id="insert">
insert into user_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userName!= null">
username,
</if>
<if test="password!=null">
password,
</if>
<if test="age!=null">
age,
</if>
<if test="gender!=null">
gender,
</if>
<if test="phone!=null">
phone,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userName!= null">
#{userName},
</if>
<if test="password!=null">
#{password},
</if>
<if test="age!=null">
#{age},
</if>
<if test="gender!=null">
#{gender},
</if>
<if test="phone!=null">
#{phone},
</if>
</trim>
</insert>
可以看到,这次能够成功的将数据插入。
查询操作
我们在购物等场景的情况下,系统会根据我们的筛选条件,动态地组装where条件。
这种其实可以看过在select语句后加上where来筛选,但由于不知道用户要选多少个条件,所以这里我们就可以用动态SQL。
public List<UserInfo> selectByCondition(UserInfo userInfo);
<select id="selectByCondition" resultType="com.example.demo.Model.UserInfo">
select * from user_info where
<trim prefixOverrides="and">
<if test="userName!=null">
username = #{userName}
</if>
<if test="password!=null">
and password = #{password}
</if>
<if test="phone!=null">
and phone = #{phone}
</if>
<if test="age!=null">
and age = #{age}
</if>
</trim>
</select>
@Test
void selectByCondition() {
UserInfo userInfo = new UserInfo();
userInfo.setUserName("小红");
userInfo.setAge(18);
userInfo.setGender(1);
userInfoXMLMapper.selectByCondition(userInfo).forEach(userInfo1 -> log.info(userInfo1.toString()));
}
当然,这样其实还会有问题,如果我们没有选条件,那么是怎么样的?
我们可以看到,这里多出来了一个where,那么应该怎么去除呢?
这里就需要用到我们的<where>标签。
where
<where>能够帮助我们自动将where子句删除,此外还能够帮助我们删除多余的and。
<select id="selectByCondition" resultType="com.example.demo.Model.UserInfo">
select * from user_info
<where>
<if test="userName!=null">
username = #{userName}
</if>
<if test="password!=null">
and password = #{password}
</if>
<if test="phone!=null">
and phone = #{phone}
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
修改操作
我们在修改信息的时候,有时也只修改一些信息,而不用全部修改,那么这样的语句应该如何实现?
也是需要用到动态SQL。
public Integer updateByCondition(UserInfo userInfo);
<update id="updateByCondition">
update user_info set
<trim suffixOverrides=",">
<if test="userName!=null">
username = #{userName},
</if>
<if test="password!=null">
password = #{password},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="gender!=null">
gender = #{gender},
</if>
<if test="phone!=null">
phone = #{phone},
</if>
</trim>
<where>
<if test="id!=null">
id = #{id}
</if>
</where>
</update>
@Test
void updateByCondition() {
UserInfo userInfo = new UserInfo();
userInfo.setId(15);
userInfo.setUserName("李四");
userInfo.setAge(20);
userInfo.setGender(1);
System.out.println(userInfoXMLMapper.updateByCondition(userInfo));
}
执行前:
执行后:
上面的SQL语句其实还存在着一定的问题:
假如没有要更新的字段,若再执行这个SQL语句,就会出错。
可以看到,会多出一个set。
那么应该如何解决呢?需要用到我们的<set>标签。
set
<set>标签可以被用于动态包含需要更新的列,而舍去其他的,例如多余的逗号。
<update id="updateByCondition">
update user_info
<set>
<if test="userName!=null">
username = #{userName},
</if>
<if test="password!=null">
password = #{password},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="gender!=null">
gender = #{gender},
</if>
<if test="phone!=null">
phone = #{phone},
</if>
</set>
<where>
<if test="id!=null">
id = #{id}
</if>
</where>
</update>
@Test
void updateByCondition() {
UserInfo userInfo = new UserInfo();
userInfo.setId(15);
userInfo.setUserName("lisi");
userInfo.setAge(18);
System.out.println(userInfoXMLMapper.updateByCondition(userInfo));
}
删除操作
使用动态SQL来写删除语句,我们可以分为两种,一种是删除一条数据,而另一种则是执行一条SQL语句后要删除多个数据。
那么这里我们就讲删除多个数据的案例,删除单条数据也可以用。
由于我们不知道有多少条数据要被删除,所以我们需要用到 <foreach>标签。
foreach
<foreach>标签可以对集合进行遍历,有以下属性:
- collection:绑定方法参数中的集合,如List、Set、Map等;
- item:遍历时的每一个对象的名称;
- open:语句块开头的字符串;
- close:语句块结束的字符串;
- separator:每次遍历之间间隔的字符串。
public Integer deleteById(List<Integer> ids);
@Test
void deleteById() {
userInfoXMLMapper.deleteById(List.of(1, 15));
userInfoXMLMapper.selectAll().forEach(userInfo -> log.info(userInfo.toString()));
}
<delete id="deleteById">
delete from user_info
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
此外,还有一个标签,适用于所有的SQL语句。
include
我们在XML文件配置中,有时会存在很多重复的片段,那么这时我们就可以将冗余的代码放到一起,需要的时候调用一下即可,这里就需要用到我们的 <include>标签。
首先我们需要用 <sql>标签将重复的片段进行封装,再通过<include>进行引用。
public List<UserInfo> selectAll2();
<sql id="BaseColumn">
id, username, password, age, gender, delete_flag ,create_time ,update_time
</sql>
<select id="selectAll2" resultType="com.example.demo.Model.UserInfo" resultMap="BaseMap">
select
<include refid="BaseColumn"></include>
from user_info
</select>
以上都是利用XML文件来使用动态SQL,那么如果我们想要用注解,就需要在XML文件中编写的语句前后再加上<script></script>
示例:
@Select("<script> select * from user_info" +
" <where>" +
" <if test=\"userName!=null\">" +
" username = #{userName}\n" +
" </if>" +
" <if test=\"password!=null\">" +
" and password = #{password}" +
" </if>" +
" <if test=\"phone!=null\">" +
" and phone = #{phone}" +
" </if>" +
" <if test=\"age!=null\">" +
" and age = #{age}" +
" </if>" +
" </where> </script>")
public List<UserInfo> selectByCondition(UserInfo userInfo);
以上就是本篇所有内容~
若有不足,欢迎指正~