本文内容基于之前的博客Spring Boot实战小技巧(三):Spring Boot + Mybatis-Plus Mybatis-Plus自带简单的数据库增删改查函数,但在实际应用中,经常会遇到实现复杂查询或循环添加等操作的需求,需要使用动态sql语句。我们可以在mapper文件,用注解的方式进行动态sql语句的编写,动态sql语句需要添加 <script> </scrpit>标签 。

1 动态查询条件
当sql语句的查询条件需要满足某种条件才能生效时,我们可以使用if标签和choose标签来实现sql查询条件的动态变化,if标签是与(and)的关系,choose是或(or)的关系。

使用if标签时,只有test条件成立,才会动态添加sql语句。test中的条件需要用\"括起来。大于号和小于号属于特殊字符,使用时需要用&gt;&lt;代替,示例如下:

@Select({
            "<script>",
            "select count(*) from tb_person",
            "<where>",
            "<if test=\"null != param.startTime\">",
            "and create_time>=#{param.startTime}",
            "</if>",
            "<if test=\"null != param.endTime\">",
            "and create_time<=#{param.endTime}",
            "</if>",
            "</where>",
            "</script>"
    })
    int countPersons(@Param("param") ParamEntity param);

choose标签是按顺序判断其内部when标签中的test条件是否成立,如果有一个成立,则choose结束。当choose中所有when的条件都不满则时,则执行otherwise中的sql。类似于Java的switch语句,choose为switch,when为case,otherwise则为default。示例如下:

@Select({
            "<script>",
            "select * from tb_person",
            "<where>",
            "<choose>",
            "<when test=\"0 == param.state\">",
            "and key_person=#{param.state} and id!='1'",
            "</when>",
            "<when test=\"1 == param.state\">",
            "and (key_person=#{param.state} or id='1')",
            "</when>",
            "<when test=\"5 == param.state\">",
            "and id='1'",
            "</when>",
            "<otherwise>",
            "</otherwise>",
            "</choose>",
            "<if test=\"null != param.name and '' != param.name\">",
            "and xm like #{param.name}",
            "</if>",
            "<if test=\"null != param.idCard and '' != param.idCard\">",
            "and sfzh like #{param.idCard}",
            "</if>",
            "<if test=\"null != param.startTime\">",
            "and create_time>=#{param.startTime}",
            "</if>",
            "<if test=\"null != param.endTime\">",
            "and create_time<=#{param.endTime}",
            "</if>",
            "</where>",
            "order by create_time desc limit #{offset},#{count}",
            "</script>"
    })
    List<Person> listPersons(@Param("param") ParamEntity param, @Param("offset") int offset, @Param("count") int count);

为安全考虑,通常会使用where标签将if和choose标签封装起来,避免第一个条件成立时sql语句会多出一个and或or。但如果有固定条件在前,即可省略where标签。示例如下:

@Select({
            "<script>",
            "select count(*) from tb_person where is_delete=0",
            "<if test=\"null != param.startTime\">",
            "and create_time>=#{param.startTime}",
            "</if>",
            "<if test=\"null != param.endTime\">",
            "and create_time<=#{param.endTime}",
            "</if>",
            "</script>"
    })
    int countPersons(@Param("param") ParamEntity param);

2 循环操作
当需要实现根据某条件进行循环查询或写入操作时,我们可以使用foreach标签。
循环写入示例如下:

@Insert({
            "<script>",
            "REPLACE INTO tb_person (uuid, id, name, dept_name, picture, age, sex, adress, create_time, is_upload, is_delete) VALUES",
            "<foreach collection='list' index='index' item='item' separator=','>",
            "(#{item.uuid}, #{item.id}, #{item.name}, #{item.deptName}, #{item.picture}, #{item.age}, #{item.sex}, #{item.address}, #{item.createTime}, #{item.isUpload}, #{item.isDelete})",
            "</foreach>",
            "</script>"
    })
    int replaceList(@Param("list") List<Person> list);

replace into跟insert功能类似,不同点在于replace into首先尝试插入数据到表中,如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据;否则,直接插入新数据。
需要注意的是,插入数据的表必须有主键或者是唯一索引,否则replace into会直接插入数据,导致表中出现重复的数据。

查询时循环匹配示例如下:

@Select({
			"<script>",
			"select task_id from tb_task",
			"where id in",
			"<foreach collection='list' index='index' item='item' separator=','>",
            "#{item.id}",
            "</foreach> )",
			"and is_delete=0",
			"</script>"
	})
	List<String> getTasks(@Param("list") List<Task> list);