MyBatis之动态SQL的使用(mybatis动态sql是做什么的,都有哪些动态sql)
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。 主要有
if
choose (when, otherwise)
trim (where, set)
foreach
数据库准备,创建一个blok的表
create table blog( id int primary key comment '博客id', title varchar(100) not null comment '博客标题', author varchar(30) not null comment '博客作者', create_time varchar(50) not null comment '创建时间', views int(30) not null comment '浏览量' ) insert into blog values(1,'javaWeb教程','黑马程序员',now(),1000) insert into blog values(2,'安卓软件开发','周世凯,陈小龙',now(),1000) insert into blog values(3,'数据结构','清华大学出版社',now(),10000) insert into blog values(4,'人文基础与应用','毛灿月',now(),560) insert into blog values(5,'java教程','小钱',now(),123456) insert into blog values(6,'C语言','谭浩强',now(),10000) insert into blog values(7,'C语言','小毛',now(),10000) 复制代码
编写实体类
@Data public class Blog { private int id; private String title; private String author; private String create_Time; private int views; } 复制代码
1、if语句
编写接口
// 通过if,查询博客 List<Blog> queryBlogIF(Map map); 复制代码
编写Mapper.xml的sql语句
<select id="queryBlogIF" parameterType="map" resultType="pojo.Blog"> select * from blog <where> <if test="title!=null"> and title=#{title} </if> <if test="author!=null"> and author=#{author} </if> </where> </select> 复制代码
如果不传入 “title”,那么所有的 Blog 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行查找并返回对应的 Blog结果
where标签会在下面介绍
测试类
@Test public void queryBlogIF(){ SqlSession sqlSession = Mybatisutil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","C语言"); map.put("author","谭浩强"); List<Blog> blogs = mapper.queryBlogIF(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); } 复制代码
结果
2、choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
编写接口
// 通过choose,查询博客 List<Blog> queryBlogChoose(Map map); 复制代码
编写Mapper.xml的sql语句
<select id="queryBlogChoose" parameterType="map" resultType="pojo.Blog"> select * from blog <where> <choose> <when test="title!=null"> title=#{title} </when> <when test="author!=null"> and author=#{author} </when> <otherwise> and views=#{views} </otherwise> </choose> </where> </select> 复制代码
测试类
//通过choose查询 @Test public void queryBlogChoose(){ SqlSession sqlSession = Mybatisutil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","C语言"); map.put("author","谭浩强"); List<Blog> blogs = mapper.queryBlogChoose(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); } 复制代码
结果
当title满足要求时,即’break‘退出choose选择,就不会执行下面sql语句的拼接,和switch一样,所以这里查出来的有两条记录,当我只传入views参数时候,就会拼接 otherwise标签的语句,结果如下
3、trim、where、set
在我们拼接语句的时候,会有标点符号,前缀后缀符号等问题,这个时候可以用trim来解决。
如下面的sql语句,假如没有使用where标签,那么如果if语句中没有满足条件的拼接语句,这个时候我们的sql语句就是select * from blog where,这样的sql语句就是错误的,那么where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<select id="queryBlogIF" parameterType="map" resultType="pojo.Blog"> select * from blog where <if test="title!=null"> and title=#{title} </if> <if test="author!=null"> and author=#{author} </if> </select> 复制代码
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim> 复制代码
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update> 复制代码
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:
<trim prefix="SET" suffixOverrides=","> ... </trim> 复制代码
3.1、set修改数据
编写接口
//使用set修改数据 int updateBlog(Map map); 复制代码
编写Mapper.xml的sql语句
<update id="updateBlog" parameterType="map"> update blog <set> <if test="title!=null"> title=#{title}, </if> <if test="author!=null"> author=#{author} </if> </set> where id=#{id} </update> 复制代码
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
测试类
@Test public void updateBlog(){ SqlSession sqlSession = Mybatisutil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); //map.put("title","C语言"); map.put("author","小浩强"); map.put("id",6); mapper.updateBlog(map); sqlSession.close(); } 复制代码
结果
4、Foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历
编写接口
//使用foreach查询3 4 5 条博客 List<Blog> queryBlogForeach(Map map); 复制代码
编写Mapper.xml的sql语句
<select id="queryBlogForeach" parameterType="map" resultType="pojo.Blog"> select * from blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select> 复制代码
测试类
@Test public void queryBlogForeach(){ SqlSession sqlSession = Mybatisutil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); ArrayList<Integer> idlist = new ArrayList<>(); idlist.add(3); idlist.add(4); idlist.add(5); map.put("idlist",idlist); List<Blog> blogs = mapper.queryBlogForeach(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); } 复制代码
结果:
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
5、SQL片段
我们会将公共的部分提取出来,方便复用
使用:
使用SQL标签提取公共的部分
<!-- 将公共部分,需要重复使用的sql提取出来,使用sql标签--> <sql id="if-titlt-author"> <if test="title!=null"> title=#{title}, </if> <if test="author!=null"> and author=#{author} </if> </sql> 复制代码
在需要使用的地方使用include标签
<!-- 在需要使用的地方使用include标签--> <select id="queryBlogIF" parameterType="map" resultType="pojo.Blog"> select * from blog <where> <include refid="if-titlt-author"></include> </where> </select> 复制代码
注意事项:
最好基于单表来定义SQL片段
不要存在where标签
总结:
动态SQl就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列就行
书写sql语句之前先在sql查询环境中测试一下,避免写错,在编写Mappre.xml的时候找BUG。
作者:浪漫主义码农
链接:https://juejin.cn/post/7030747839223300132