MyBatis令人喜欢的一大特性就是动态SQL。在使用JDBC的过程中, 根据条件进行SQL的拼接是很麻烦且很容易出错的。MyBatis动态SQL的出现, 解决了这个麻烦。MyBatis通过OGNL来进行动态SQL的使用的。

文章目录

  • 动态SQL标签
  • 数据准备
  • if 标签
    • 在WHERE条件中使用 if 标签
      • 查询条件
      • 动态SQL
    • 在UPDATE更新列中使用 if 标签
      • 更新条件
      • 动态 SQL
    • 在INSERT动态插入中使用if标签
      • 插入条件
      • 动态 SQL
  • choose 标签
    • 查询条件
    • 动态 SQL
  • trim(set、where)
    • where
      • 查询条件
      • 动态 SQL
    • set
    • trim
      • trim 来表示 where
      • trim 来表示 set
      • trim 的几个属性
  • foreach 标签
    • 在 where 中使用 foreach
      • 查询条件
      • 动态 SQL
    • foreach 实现批量插入
      • 动态SQL
  • bind 标签
  • 本文小结

动态SQL标签

常见的动态sql标签如下所示

元素 作用 备注
if 判断语句 单条件分支
choose(when、otherwise) 相当于 Java 中的 if else 多条件分支
itrim(where、set) 辅助元素 用于处理 SQL 拼接问题
foreach 循环语句 批量插入, 更新, 查询时经常用到
bind 创建一个变量, 并绑定到上下文中 用于兼容不同的数据库, 防止 SQL 注入等

数据准备

DROP TABLE IF EXISTS `student`;CREATE TABLE `student` (`student_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',`name` varchar(20) DEFAULT NULL COMMENT '姓名',`phone` varchar(20) DEFAULT NULL COMMENT '电话',`email` varchar(50) DEFAULT NULL COMMENT '邮箱',`sex` tinyint(4) DEFAULT NULL COMMENT '性别',`locked` tinyint(4) DEFAULT NULL COMMENT '状态(0:正常,1:锁定)',`gmt_created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '存入数据库的时间',`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改的时间',`delete` int(11) DEFAULT NULL,PRIMARY KEY (`student_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';

if 标签

if 标签是我们最常使用的。在查询、删除、更新的时候很可能会使用到。必须结合 test 属性联合使用。

在WHERE条件中使用 if 标签

这是常见的一种现象, 我们在进行按条件查询的时候, 可能会有多种情况。

查询条件

根据输入的学生信息进行条件检索

  • 当只输入用户名时, 使用用户名进行模糊检索;

动态SQL

xml文件

 <select id="selectByStudentSelective" resultType="cn.wideth.entity.sql.Student">SELECT T.* FROM student Twhere 1=1<if test="name != null and name !=''">and name like concat('%', #{name}, '%')</if></select>

测试函数

package cn.wideth.util.sql;import cn.wideth.PdaAndIpadApplication;
import cn.wideth.entity.sql.Student;
import cn.wideth.mapper.StudentMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class StudentTest {@Autowiredprivate StudentMapper studentMapper;@Testpublic  void  studentInfoTest(){String name = "王";List<Student> data = studentMapper.selectByStudentSelective(name);System.out.println(data);}
}

在UPDATE更新列中使用 if 标签

有时候我们不希望更新特定的字段,比如当姓名为空时,不进行更新。

更新条件

只更新有变化的字段, 空值不更新。

动态 SQL

xml文件

 <update id="updateByPrimaryKeySelective"  parameterType="cn.wideth.entity.sql.Student">update student<set><if test="name != null">`name` = #{name,jdbcType=VARCHAR},</if><if test="phone != null">phone = #{phone,jdbcType=VARCHAR},</if><if test="email != null">email = #{email,jdbcType=VARCHAR},</if><if test="sex != null">sex = #{sex,jdbcType=TINYINT},</if><if test="locked != null">locked = #{locked,jdbcType=TINYINT},</if><if test="gmtCreated != null">gmt_created = #{gmtCreated,jdbcType=TIMESTAMP},</if><if test="gmtModified != null">gmt_modified = #{gmtModified,jdbcType=TIMESTAMP},</if></set>where student_id = #{studentId,jdbcType=INTEGER}

测试函数

package cn.wideth.util.sql;import cn.wideth.PdaAndIpadApplication;
import cn.wideth.entity.sql.Student;
import cn.wideth.mapper.StudentMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class StudentTest {@Autowiredprivate StudentMapper studentMapper;@Testpublic  void  studentInfoTest(){Student student = new Student();student.setStudentId(1);student.setName("明明");student.setPhone("13838438888");System.out.println(studentMapper.updateByPrimaryKeySelective(student));}
}

在INSERT动态插入中使用if标签

我们插入数据库中的一条记录, 不是每一个字段都有值的, 而是动态变化的。在这时候使用 if 标签, 可帮我们解决这个问题。

插入条件

只有非空属性才插入。

动态 SQL

xml文件

 <insert id="insertSelective" parameterType="cn.wideth.entity.sql.Student">insert into student<trim prefix="(" suffix=")" suffixOverrides=","><if test="studentId != null">student_id,</if><if test="name != null">`name`,</if><if test="phone != null">phone,</if><if test="email != null">email,</if><if test="sex != null">sex,</if><if test="locked != null">locked,</if><if test="gmtCreated != null">gmt_created,</if><if test="gmtModified != null">gmt_modified,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="studentId != null">#{studentId,jdbcType=INTEGER},</if><if test="name != null">#{name,jdbcType=VARCHAR},</if><if test="phone != null">#{phone,jdbcType=VARCHAR},</if><if test="email != null">#{email,jdbcType=VARCHAR},</if><if test="sex != null">#{sex,jdbcType=TINYINT},</if><if test="locked != null">#{locked,jdbcType=TINYINT},</if><if test="gmtCreated != null">#{gmtCreated,jdbcType=TIMESTAMP},</if><if test="gmtModified != null">#{gmtModified,jdbcType=TIMESTAMP},</if></trim></insert>

测试函数

package cn.wideth.util.sql;import cn.wideth.PdaAndIpadApplication;
import cn.wideth.entity.sql.Student;
import cn.wideth.mapper.StudentMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class StudentTest {@Autowiredprivate StudentMapper studentMapper;@Testpublic  void  studentInfoTest(){Student student = new Student();student.setName("小飞机");student.setPhone("13838438899");student.setEmail("xiaofeiji@qq.com");student.setLocked(0);System.out.println(studentMapper.insertSelective(student));}
}

choose 标签

choose when otherwise 标签可以帮我们实现 if else 的逻辑。一个 choose 标签至少有一个 when, 最多一个otherwise。

下面是一个查询的例子

查询条件

假设 name 具有唯一性, 查询一个学生

  • 当 studen_id 有值时, 使用 studen_id 进行查询;
  • 当 studen_id 没有值时, 使用 name 进行查询;
  • 否则返回空

动态 SQL

xml文件

<select id="selectByIdOrName" resultType="cn.wideth.entity.sql.Student">selectT.*from student Twhere 1=1<choose><when test="studentId != null">and student_id=#{studentId}</when><when test="name != null and name != ''">and name=#{name}</when><otherwise>and 1=2</otherwise></choose></select>

测试函数

package cn.wideth.util.sql;import cn.wideth.PdaAndIpadApplication;
import cn.wideth.entity.sql.Student;
import cn.wideth.mapper.StudentMapper;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class StudentTest {@Autowiredprivate StudentMapper studentMapper;@Testpublic  void  studentInfoTest(){Student student = new Student();student.setName("小飞机");student.setStudentId(1);Student studentById = studentMapper.selectByIdOrName(student);System.out.println("有 ID 则根据 ID 获取");System.out.println(ToStringBuilder.reflectionToString(studentById, ToStringStyle.MULTI_LINE_STYLE));student.setStudentId(null);Student studentByName = studentMapper.selectByIdOrName(student);System.out.println("没有 ID 则根据 name 获取");System.out.println(ToStringBuilder.reflectionToString(studentByName, ToStringStyle.MULTI_LINE_STYLE));student.setName(null);Student studentNull = studentMapper.selectByIdOrName(student);System.out.println("没有 ID 和 name, 返回 null");}
}

trim(set、where)

这三个其实解决的是类似的问题。如我们在写前面的[在 WHERE 条件中使用 if 标签] SQL 的时候, where 1=1 这个条件我们是不希望存在的。

where

查询条件

根据输入的学生信息进行条件检索。

  • 当只输入用户名时, 使用用户名进行模糊检索;
  • 当只输入性别时, 使用性别进行完全匹配
  • 当用户名和性别都存在时, 用这两个条件进行查询匹配查询

不使用 where 1=1。

动态 SQL

很显然, 我们要解决这几个问题

  • 当条件都不满足时:此时 SQL 中应该要不能有 where , 否则导致出错
  • 当 if 有条件满足时:SQL 中需要有 where, 且第一个成立的 if 标签下的 and | or 等要去掉

这时候, 我们可以使用 where 标签。

xml文件

<select id="selectByStudentSelectiveWhereTag" resultType="cn.wideth.entity.sql.Student">select T.*from student T<where><if test="name != null and name !=''">and name like concat('%', #{name}, '%')</if><if test="sex != null">and sex=#{sex}</if></where></select>

测试函数

package cn.wideth.util.sql;import cn.wideth.PdaAndIpadApplication;
import cn.wideth.entity.sql.Student;
import cn.wideth.mapper.StudentMapper;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class StudentTest {@Autowiredprivate StudentMapper studentMapper;@Testpublic  void  studentInfoTest(){Student search = new Student();search.setName("明");System.out.println("只有名字时的查询");List<Student> studentsByName = studentMapper.selectByStudentSelectiveWhereTag(search);for (int i = 0; i < studentsByName.size(); i++) {System.out.println(ToStringBuilder.reflectionToString(studentsByName.get(i), ToStringStyle.MULTI_LINE_STYLE));}search.setSex(1);System.out.println("姓名和性别同时存在的查询");List<Student> studentsBySex = studentMapper.selectByStudentSelectiveWhereTag(search);for (int i = 0; i < studentsBySex.size(); i++) {System.out.println(ToStringBuilder.reflectionToString(studentsBySex.get(i), ToStringStyle.MULTI_LINE_STYLE));}System.out.println("姓名和性别都不存在时查询");search.setName(null);search.setSex(null);List<Student> studentsByNameAndSex = studentMapper.selectByStudentSelectiveWhereTag(search);for (int i = 0; i < studentsByNameAndSex.size(); i++) {System.out.println(ToStringBuilder.reflectionToString(studentsByNameAndSex.get(i), ToStringStyle.MULTI_LINE_STYLE));}}
}

set

set 标签也类似, 在 [2.2 在 UPDATE 更新列中使用 if 标签] 中, 如果我们的方法updateByPrimaryKeySelective 没有使用 标签, 那么我们就要想办法处理字段全为空的条件, 字段不为空的条件等。有了这个, 我们只需要写 if 标签即可, 不需要处理类似的问题。


trim

set 和 where 其实都是 trim 标签的一种类型, 该两种功能都可以使用 trim 标签进行实现。

trim 来表示 where

如以上的 where 标签, 我们也可以写成

<trim prefix="where" prefixOverrides="AND |OR">
</trim>

表示当 trim 中含有内容时, 添加 where, 且第一个为 and 或 or 时, 会将其去掉。而如果没有内容, 则不添加 where。

trim 来表示 set

相应的, set 标签可以如下表示

<trim prefix="SET" suffixOverrides=",">
</trim>

表示当 trim 中含有内容时, 添加 set, 且最后的内容为 , 时, 会将其去掉。而没有内容, 不添加 set

trim 的几个属性

  • prefix: 当 trim 元素包含有内容时, 增加 prefix 所指定的前缀
  • prefixOverrides: 当 trim 元素包含有内容时, 去除 prefixOverrides 指定的 前缀
  • suffix: 当 trim 元素包含有内容时, 增加 suffix 所指定的后缀
  • suffixOverrides:当 trim 元素包含有内容时, 去除 suffixOverrides 指定的后缀

foreach 标签

foreach 标签可以对数组, Map 或实现 Iterable 接口。

foreach 中有以下几个属性

  • collection: 必填, 集合/数组/Map的名称.
  • item: 变量名。即从迭代的对象中取出的每一个值
  • index: 索引的属性名。当迭代的对象为 Map 时, 该值为 Map 中的 Key.
  • open: 循环开头的字符串
  • close: 循环结束的字符串
  • separator: 每次循环的分隔符

其他的比较好理解, collection 中的值应该怎么设定呢?

跟接口方法中的参数相关。

只有一个数组参数或集合参数

默认情况:集合collection=list, 数组是collection=array

推荐:使用 @Param 来指定参数的名称, 如我们在参数前@Param(“ids”), 则就填写 collection=ids

多参数

多参数请使用 @Param 来指定, 否则SQL中会很不方便

参数是Map

指定为 Map 中的对应的 Key 即可。其实上面的 @Param 最后也是转化为 Map 的。

参数是对象

使用属性.属性即可。


在 where 中使用 foreach

在 where条件中使用, 如按id集合查询, 按id集合删除等。

查询条件

我们希望查询用户 id 集合中的所有用户信息。

动态 SQL

xml文件

 <select id="selectByStudentIdList" resultType="cn.wideth.entity.sql.Student">select T.*from student Twhere student_id in<foreach collection="list" item="id" open="(" close=")" separator="," index="i">#{id}</foreach></select>

测试函数

package cn.wideth.util.sql;import cn.wideth.PdaAndIpadApplication;
import cn.wideth.entity.sql.Student;
import cn.wideth.mapper.StudentMapper;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.LinkedList;
import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class StudentTest {@Autowiredprivate StudentMapper studentMapper;@Testpublic  void  studentInfoTest(){List<Integer> ids = new LinkedList<>();ids.add(1);ids.add(3);List<Student> students = studentMapper.selectByStudentIdList(ids);for (int i = 0; i < students.size(); i++) {System.out.println(ToStringBuilder.reflectionToString(students.get(i), ToStringStyle.MULTI_LINE_STYLE));}}
}

foreach 实现批量插入

可以通过foreach来实现批量插入。

动态SQL

xml文件

<insert id="insertList">insert into student(name, phone, email, sex, locked)values<foreach collection="list" item="student" separator=",">(#{student.name}, #{student.phone},#{student.email},#{student.sex},#{student.locked})</foreach></insert>

测试函数

package cn.wideth.util.sql;import cn.wideth.PdaAndIpadApplication;
import cn.wideth.entity.sql.Student;
import cn.wideth.mapper.StudentMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.LinkedList;
import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class StudentTest {@Autowiredprivate StudentMapper studentMapper;@Testpublic  void  studentInfoTest(){List<Student> students = new LinkedList<>();Student stu1 = new Student();stu1.setName("批量01");stu1.setPhone("13888888881");stu1.setLocked( 0);stu1.setEmail("13888888881@138.com");stu1.setSex(1);students.add(stu1);Student stu2 = new Student();stu2.setName("批量02");stu2.setPhone("13888888882");stu2.setLocked(0);stu2.setEmail("13888888882@138.com");stu2.setSex(0);students.add(stu2);System.out.println(studentMapper.insertList(students));}
}

bind 标签

bind 标签是通过 OGNL 表达式去定义一个上下文的变量, 这样方便我们使用。

如在 selectByStudentSelective 方法中, 有如下

<if test="name != null and name !=''">and name like concat('%', #{name}, '%')</if>

在 MySQL 中, 该函数支持多参数, 但在 Oracle 中只支持两个参数。那么我们可以使用 bind 来让该 SQL 达到支持两个数据库的作用

<if test="name != null and name !=''"><bind name="nameLike" value="'%'+name+'%'"/>and name like #{nameLike}
</if>

本文小结

本文总结了MyBatis动态SQL使用,帮助大家更加优雅的使用sql。

MyBatis动态SQL使用相关推荐

  1. MyBatis动态SQL之 set 和 trim标记的使用示例

    2019独角兽企业重金招聘Python工程师标准>>> 和之前的where一样,set和trim也是智能标记 在之前的user.xml中添加 <update id=" ...

  2. MyBatis动态SQL(认真看看, 以后写SQL就爽多了)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:cnblogs.com/homejim/p/9909657. ...

  3. 9.mybatis动态SQL标签的用法

    mybatis动态SQL标签的用法 动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦 ...

  4. 认真看看, 以后写 SQL 就爽多了:MyBatis 动态 SQL:

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 阿进的写字台 来源 | cnblogs.com ...

  5. MyBatis动态SQL,写SQL更爽

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客: ...

  6. 认真看看, 以后写 SQL 就爽多了:MyBatis 动态 SQL

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达今日推荐:2020年7月程序员工资统计,平均14357元,又跌了,扎心个人原创100W+访问量博客:点击前往,查看更多 整理 ...

  7. MyBatis——动态SQL语句——if标签和where标签复合使用

    功能需求 根据性别和名字查询用户 官方文档 MyBatis--动态 SQL SQL语句 SELECT id, username, birthday, sex, address FROM `user` ...

  8. Mybatis 动态sql语句(if标签和where标签)

    功能:根据性别和名字查询用户 查询sql语句: SELECT id, username, birthday, sex, address FROM `user` WHERE sex = 1 AND us ...

  9. mybatis注解动态sql_超全MyBatis动态SQL详解

    MyBatis 令人喜欢的一大特性就是动态 SQL.在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的.MyBatis 动态 SQL 的出现, 解决了这个麻烦. MyBa ...

  10. Mybatis 动态Sql语句《常用》

    MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不能忘了必要的空格,还要注意省掉 ...

最新文章

  1. Android之一起玩转下拉刷新控件
  2. mysql 取出20条数据_“取出数据表中第10条到第20条记录”的sql语句+select top 使用方法...
  3. 前端将数据转化为弹幕效果的实现方式
  4. 论林耐斯-Linux系统的重要性
  5. Flask框架——请求钩子与request请求对象
  6. java半解释半编译_编译型语言、解释型语言、半编译半解释型语言
  7. c语言如何在手机上运行程序,各位前辈这两个程序怎么在手机上运行
  8. 常见设计规范与 Sketch 源文件下载集合
  9. 【自然语言处理】浅谈语料库
  10. win7下ApmServ启动失败问题
  11. matlab hist3 密度图,Matlab中hist3
  12. 小葵花妈妈课堂之Nginx Rewirte
  13. bootstrap Less
  14. 微信小程序调取微信支付遇到的问题“商户号该产品权限未开通,请前往商户平台产品中心检查后重试”
  15. 美化你的Typora
  16. 学习日志——2019/07/04
  17. 本以为大厂无望,结果陆续收到京东/滴滴/爱奇艺offer的我迷茫了
  18. matlab程序运行完后响铃,matlab基本
  19. Vue源码实现之watcher拾遗
  20. 被动信息收集(一)nslookup.dig.DNS字典爆破.whois

热门文章

  1. 为view添加约束constraints
  2. ViewPager切换滑动速度修改
  3. Google Maps API 申请方式变更为APIs Console, android手机申请方式
  4. oracle 10g SQL* Plus主机字符串
  5. 通过meta进行重定向
  6. CSS3 -- display:flex
  7. Eclipse SVN冲突解决方案
  8. HTTP之Cookie
  9. Tomcat修改favicon.ico图标,Linux下Tomcat修改favicon.ico图标,Tomcat更换favicon.ico图标...
  10. Oracle中关于并集/交集/差集的运算