ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

mybatis(二)

mybatis(二)

一,动态SQL

动态SQL是Mybatis的强大特性之一,能够完成不同条件下不同的sql拼接

1.1 <if>标签

在注册用户的时候,可能会有这样⼀个问题:

注册分为两种字段:必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢?

这个时候就需要使用动态标签来判断了,比如添加的时候性别gender为非必填字段,具体实现如 下:

接口定义:

Integer insertUserInfoByCondition(UserInfo userInfo);

Mapper.xml实现:

<insert id="insertUserInfoByCondition"> insert into user_info(username,`password`,age, <if test="gender!=null"> gender, </if> phone) values (#{username},#{password},#{age}, <if test="gender!=null"> #{gender}, </if> #{phone}) </insert>

不加<if>标签时,运行结果如下:

未传参的会赋值为null

加了标签之后,运行结果为:

其中未传参的会赋上默认值

或者使用注解方式

@Insert(""" <script> insert into user_info <trim suffixOverrides="," prefix="(" suffix=")"> <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 suffixOverrides="," prefix="(" suffix=")"> <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> </script> """) Integer insertUserInfoByCondition(UserInfo userInfo);

注意test中的gender,是传入对象中的属性,不是数据库字段(上述例子中,test里面是判断条件,判断 #{gender}是否为空)

Q: 可不可以不进行判断,直接把字段设置为null呢?

A: 不可以, 这种情况下,如果gender字段有默认值,就会设置为默认值

1.2 <trim>标签

之前的插入用户功能,只是有⼀个gender字段可能是选填项,如果有多个字段,⼀般考虑使用标签结合标签,对多个字段都采取动态生成的方式。

标签中有如下属性:

• prefix:表示整个语句块,以prefix的值作为前缀

• suffix:表示整个语句块,以suffix的值作为后缀

• prefixOverrides:表示整个语句块要去除掉的前缀

• suffixOverrides:表示整个语句块要去除掉的后缀

调整Mapper.xml的插入语句为:

<insert id="insertUserInfoByCondition"> insert into user_info <trim suffixOverrides="," prefix="(" suffix=")"> <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 suffixOverrides="," prefix="(" suffix=")"> <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>

在以上sql动态解析时,会将第⼀个部分做如下处理:

• 基于 prefix 配置,开始部分加上 (

• 基于 suffix 配置,结束部分加上 )

• 多个组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于suffixOverrides 配置去掉最后一个,

• 注意 username 是传入对象的属性

1.3 <where>标签

需求:传⼊的⽤⼾对象,根据属性做where条件查询,⽤⼾对象中属性不为null的,都为查询条件.如 username为"a",则查询条件为whereusername="a"

接⼝定义:

List<UserInfo> queryByCondition();

Mapper.xml实现:

<select id="queryByCondition" resultType="com.luoyaoyun.mybatisdemo.model.UserInfo"> <!-- SELECT * FROM user_info WHERE 1=1--> <!-- <trim prefixOverrides="AND">--> <!-- <if test="age!=null">--> <!-- age = #{age}--> <!-- </if>--> <!-- <if test="gender!=null">--> <!-- AND gender = #{gender}--> <!-- </if>--> <!-- <if test="deleteFlag!=null">--> <!-- AND delete_flag = #{deleteFlag}--> <!-- </if>--> <!-- </trim>--> SELECT * FROM user_info <where> <if test="age!=null"> age = #{age} </if> <if test="gender!=null"> AND gender = #{gender} </if> <if test="deleteFlag!=null"> AND delete_flag = #{deleteFlag} </if> </where> </select>

上述代码有两种方法,其中where标签就完成了第一种方法中实现的两种方法,1)去除最前面的and 2)如果查询条件为空,则不添加where关键字

<where>只会在⼦元素有内容的情况下才插⼊where⼦句,⽽且会⾃动去除⼦句的开头的AND或 OR

以上标签也可以使用

<trim prefix="where" prefixOverrides="AND">

替换,但是此种情况下,当⼦元素都没有内容时,where关键字也会保留

1.4 <set>标签

需求:根据传入的⽤⼾对象属性来更新⽤⼾数据,可以使⽤标签来指定动态内容

接⼝定义:根据传⼊的⽤⼾id属性,修改其他不为null的属性

Integer updateUserByCondition(UserInfo userInfo);

Mapper.xml

<update id="updateByCondition"> <!-- update user_info--> <!-- set--> <!-- <trim suffixOverrides=",">--> <!-- <if test="username!=null">--> <!-- username=#{username},--> <!-- </if>--> <!-- <if test="age!=null">--> <!-- age= #{age},--> <!-- </if>--> <!-- <if test="gender!=null">--> <!-- gender = #{gender},--> <!-- </if>--> <!-- <if test="deleteFlag!=null">--> <!-- delete_flag = #{deleteFlag}--> <!-- </if>--> <!-- </trim>--> <!-- where id= #{id}--> update user_info <set> <if test="username!=null"> username=#{username}, </if> <if test="age!=null"> age= #{age}, </if> <if test="gender!=null"> gender = #{gender}, </if> <if test="deleteFlag!=null"> delete_flag = #{deleteFlag} </if> </set> where id= #{id} </update>

<set> : 动态的在SQL语句中插⼊set关键字,并会删掉额外的逗号.(用于update语句中)

1.5 <foreach>标签

对集合进⾏遍历时可以使⽤该标签。标签有如下属性:

• collection:绑定⽅法参数中的集合,如List,Set,Map或数组对象

• item:遍历时的每⼀个对象

• open:语句块开头的字符串

• close:语句块结束的字符串

• separator:每次遍历之间间隔的字符串

需求:根据多个userid,删除用户数据

接⼝⽅法:

void deleteByIds(List<Integer> ids);

UserInfoMapper.xml

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

1.6 <include>标签

在xml映射⽂件中配置的SQL,有时可能会存在很多重复的⽚段,此时就会存在很多冗余的代码

我们可以对重复的代码⽚段进⾏抽取,将其通过<sql> 标签封装到⼀个SQL⽚段,然后再通过

<include>标签进⾏引⽤。

  • <sql>:定义可重⽤的SQL⽚段
  • <include>:通过属性refid,指定包含的SQL⽚段
<sql id="allColumn"> id, username, age, gender, phone, delete_flag, create_time, update_time </sql>

通过<include>标签在原来抽取的地⽅进⾏引⽤。

<select id="queryAllUser" resultMap="BaseMap"> select <include refid="allColumn"></include> from user_info </select> <select id="queryById" resultType="com.example.demo.model.UserInfo"> select <include refid="allColumn"></include> from user_info where id= #{id} </select>

二. 案例练习

2.1 表白墙

2.1.1 数据准备

DROP TABLE IF EXISTS message_info; CREATE TABLE `message_info` ( `id` INT ( 11 ) NOT NULL AUTO_INCREMENT, `from` VARCHAR ( 127 ) NOT NULL, `to` VARCHAR ( 127 ) NOT NULL, `message` VARCHAR ( 256 ) NOT NULL, `delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0正常, 1删除', `create_time` DATETIME DEFAULT now(), `update_time` DATETIME DEFAULT now() ON UPDATE now(), PRIMARY KEY ( `id` ) ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;

ON UPDATE now():当数据发生更新操作时,自动把该列的值设置为now(),

now() 可以替换成其他获取时间的标识符,⽐如:CURRENT_TIMESTAMP(),LOCALTIME()等

2.1.2 引入MyBatis和MySQL驱动依赖

修改pom文件:

<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>

2.1.4 编写后端代码

Model

@Data public class MessageInfo { private Integer id; private String from; private String to; private String message; private Integer deleteFlag; private Date createTime; private Date updateTime; }

MessageInfoMapper

@Mapper public interface MessageInfoMapper { @Select("select `id`, `from`, `to`, `message` from message_info where delete_flag=0") List<MessageInfo> queryAll(); @Insert("insert into message_info (`from`,`to`, `message`) values(#{from},#{to},#{message})") Integer addMessage(MessageInfo messageInfo); }

MessageInfoService

@Service public class MessageInfoService { @Autowired private MessageInfoMapper messageInfoMapper; public List<MessageInfo> queryAll() { return messageInfoMapper.queryAll(); } public Integer addMessage(MessageInfo messageInfo) { return messageInfoMapper.addMessage(messageInfo); } }

MessageController

@RequestMapping("/message") @RestController public class MessageController { @Autowired private MessageInfoService messageInfoService; @GetMapping("/getList") public List<MessageInfo> getList(){ return messageInfoService.queryAll(); } @PostMapping(value = "/publish") public boolean publish(MessageInfo messageInfo) { System.out.println(messageInfo); if (StringUtils.hasLength(messageInfo.getFrom()) && StringUtils.hasLength(messageInfo.getTo()) && StringUtils.hasLength(messageInfo.getMessage())) { messageInfoService.addMessage(messageInfo); return true; } return false; } }

访问这个网址即可

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进