MyBatis系列(九)- MyBatis的动态SQL| 8


相关文章

MyBatis系列汇总:MyBatis系列


前言

  • 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
  • 上面这句话是MaBatis官网说的!这篇文章很重要!在工作中必不可少!
  • 首先我们先建立一些测试的库。下面的示例都是建立在此表进行测试的!
+ ![image-20210730140037206.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/b096b52bd7af661e0318970b8bacda4305487bde5190fb4a72d6e46500329c47)
  • 实体类
+ 
1
2
3
4
5
6
7
8
9
10
java复制代码@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
private Integer id;
private String title;
private String autor;
private Date creat_time;
private Integer reads;
}
  • 使用动态标签,可以在xml中也就是sql中写一些基本的逻辑。十分方便好用!
  • 造点数据,方便测试。
+ ![image-20210730161726094.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/c4da5bb1c6147761f4da4dfc31293c3bd6c0c12555bd6ae173b61ad373d23c54)

一、if 、where 标签

  • xml
+ 
1
2
3
4
5
6
7
8
9
10
11
java复制代码    <select id="getBlogInfo" resultType="Blog" parameterType="map">
select
*
from
myblog
where
1 = 1
<if test="title!='' and title!=null">
and title like concat(concat('%',#{title}),'%')
</if>
</select>
  • mapper
+ 
1
java复制代码List<Blog> getBlogInfo(Map<String,Object> map);
+ 在实际工作开发中,我们传入的值一般使用map来。这样会更加方便和易扩展。 + 而返回的result,我们一般使用实体类来实现,因为Swgger,前后端联调无敌方便!!强烈推荐! + 如果有小伙伴想了解Swgger的话,欢迎留言,博主会根据需要的程度来决定是否单独开一篇来详解Swgger!!!
  • Junit Test
+ 
1
2
3
4
5
6
7
8
9
10
11
12
java复制代码    @Test
public void getMyBlog(){
SqlSession session = MybatisUtils.getSession();
MyBlogMapper mapper = session.getMapper(MyBlogMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("title","Mybatis");
List<Blog> myBlogMappers = mapper.getBlogInfo(map);
for (Blog myBlogMapper : myBlogMappers) {
System.out.println(myBlogMapper);
}
session.close();
}
  • 执行结果:
+ ![image-20210730162207384.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/ad21c2d2fd466aec566e4a0fa108c6d70521755a8986c301ebc91698600b6e1d)
+ 完美查出。
  • 可能有人会问,为什么要加这个标签呢?
+ 首先,if 标签可以让我们的sql更加灵活,这个 title 的值传的话就代表带条件查询,不传的话,即是无效代码,查询所有!
+ 然后,如果在service中实现这种效果也是可以的,但是十分繁琐。所以在实际工作中,if 标签是用的最多的!
+ 最后,同学们注意到上面sql中 `1=1`,为啥要写这个,在实际开发中,我们一般都是软删除(逻辑删除),所以一般都会有个删除标识来确定这条数据是否存在!加这个纯粹是为了模拟真实开发代码!
  • 那么where标签什么时候用呢?
+ xml


    - 
1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码    <select id="getBlogInfo1" resultType="Blog" parameterType="map">
select
*
from
myblog
where
<if test="title!='' and title!=null">
title like concat(concat('%',#{title}),'%')
</if>
<if test="id!='' and id!=null">
and id = #{id}
</if>
</select>
- 如果我们的语句是这样子的,那么如果 title 为空,sql语句是不是相当于where后面直接加上了and? - 演示: *
1
2
3
4
5
6
7
8
9
10
11
12
java复制代码    @Test
public void getMyBlog1(){
SqlSession session = MybatisUtils.getSession();
MyBlogMapper mapper = session.getMapper(MyBlogMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("id","2");
List<Blog> myBlogMappers = mapper.getBlogInfo1(map);
for (Blog myBlogMapper : myBlogMappers) {
System.out.println(myBlogMapper);
}
session.close();
}
* 报错信息如下 * ![image-20210730163358590.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/db124eb5c6db6f1048d8bee9623298575c39c92adb56923278ff292763d755b9) *
1
2
3
4
5
6
7
java复制代码org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and id = '2'' at line 8
### The error may exist in com/dy/dynamic/mapper/MyBlogMapper.xml
### The error may involve com.dy.dynamic.mapper.MyBlogMapper.getBlogInfo1-Inline
### The error occurred while setting parameters
### SQL: select * from myblog where and id = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and id = '2'' at line 8
  • 使用where标签
+ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码    <select id="getBlogInfo1" resultType="Blog" parameterType="map">
select
*
from
myblog
<where>
<if test="title!='' and title!=null">
title like concat(concat('%',#{title}),'%')
</if>
<if test="id!='' and id!=null">
and id = #{id}
</if>
</where>
</select>
+ 执行看结果 + ![image-20210730163517626.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/7bdb5fc20fa1469b8d89607a5498c3bd27108f47270a128f9d193ddc8f7a8389) + 完美解决!
  • 如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
+ 
1
2
java复制代码SELECT * FROM BLOG
WHERE
  • 这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:
+ 
1
2
java复制代码SELECT * FROM BLOG
WHERE AND id = 2
+ 这个查询也会失败。这个问题不能简单地用条件元素来解决。
  • where标签的作用显而易见了!
+ 当条件有and时,它可以判断该条件是否是第一个条件,是的话自动去除。
+ *where* 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,*where* 元素也会将它们去除。
  • 如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
+ 
1
2
3
java复制代码<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
+ 这个trim 下面再讲。

二、choose、when、otherwise 标签

  • 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
  • if 讲完了,记不记得还有一种判断条件的东东?
+ 没错就是 switch case (JAVA中)
+ 在动态sql中就是 choose
+ 下面来玩一玩这玩意
  • xml
+ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码    <select id="getBlogInfoWhoose" resultType="Blog" parameterType="map">
select
*
from
myblog
<where>
<choose>
<when test="title!='' and title!=null">
title like concat(concat('%',#{title}),'%')
</when>
<when test="id!='' and id!=null">
and id = #{id}
</when>
<otherwise>
AND `reads` > 10000
</otherwise>
</choose>
</where>
</select>
  • mapper
+ 
1
java复制代码List<Blog> getBlogInfoWhoose(Map<String,Object> map);
  • Junit Test
+ 
1
2
3
4
5
6
7
8
9
10
11
12
java复制代码    @Test
public void getMyBlog1(){
SqlSession session = MybatisUtils.getSession();
MyBlogMapper mapper = session.getMapper(MyBlogMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("title","Spring");
List<Blog> myBlogMappers = mapper.getBlogInfoWhoose(map);
for (Blog myBlogMapper : myBlogMappers) {
System.out.println(myBlogMapper);
}
session.close();
}
  • 执行结果
+ ![image-20210730165601475.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/d38a88bf93adae23d1f56776a54fc6153a7f2d5eebf2e1ca784b3589402fc838)
  • 当没有满足choose中的条件时
+ Junit Test


    - 
1
2
3
4
5
6
7
8
9
10
11
12
java复制代码@Test
public void getMyBlog1(){
SqlSession session = MybatisUtils.getSession();
MyBlogMapper mapper = session.getMapper(MyBlogMapper.class);
Map<String,Object> map = new HashMap<>();
// map.put("title","Spring");
List<Blog> myBlogMappers = mapper.getBlogInfoWhoose(map);
for (Blog myBlogMapper : myBlogMappers) {
System.out.println(myBlogMapper);
}
session.close();
}
+ 执行结果 - ![image-20210730165825240.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/208a61c46747481b02e46d2897b6a99c3ce24397de1aaec8a9d2c14dc70dc558) - 这样只查出来readis(阅读量)大于10000的数据
  • 总结下这几个标签
+ 类似reads这种关键字,需要加飘号 `
+ 所有条件都不满足的时候就输出 otherwise 中的内容。
+ when元素表示当 when 中的条件满足的时候就输出其中的内容,跟 JAVA 中的 switch 效果差不多的是按照条件的顺序。

三、trim、set 标签

  • 上面说的这么多,都是在讲select(查询),那么现在来讲一下 update 中的标签 set
  • 基本的update语句我这里就不再重复写了,大家只需要关注这种动态语句即可。在实际工作中开发一般以这种居多,毕竟它比较灵活嘛!

①、set 标签

  • xml
+ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码    <update id="updateBlogName" parameterType="map">
update myblog
<set>
<if test="title!=null and title!=''">
title = #{title},
</if>
<if test="autor!=null and autor!=''">
autor = #{autor},
</if>
<if test="reads!=null and reads!=''">
`reads` = #{reads}
</if>
</set>
where
id = #{id}
</update>
  • mapper
+ 
1
java复制代码Integer updateBlogName(Map<String,Object> map);
  • Junit Test
+ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码    @Test
public void updateBlogName(){
SqlSession session = MybatisUtils.getSession();
MyBlogMapper mapper = session.getMapper(MyBlogMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("id","3");
map.put("title","富婆让我陪她逛街");
map.put("autor","大大大大鱼");
map.put("reads","100000");
Integer num = mapper.updateBlogName(map);
System.out.println("一共更新了:"+num+"条数据");
session.commit();//更新不要忘记提交事务哦
session.close();
}
  • 执行结果
+ ![image-20210731162506916.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/f68a8083bc07ec71ee7f61443dfed55ab10b350fcde72e9285f5af33e67a83a2)
+ ![image-20210731162711474.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/7f96f033667986cd05e83ce80f38024fbd55e517a1583613e5deb01a79164fc9)
  • 大家有没有发现,我们在sql中是将set语句后面的逗号写死的?
+ ![image-20210731163403210.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/e22d2c8bdbd5cedbfe0f700091b173a4c76e87e000bf457c54c28ab73f4415b8)
+ 那么,如果我们只传了`title` 和`autor`呢?
+ 理论上是不是语句变成这样?


    - 
1
2
3
4
java复制代码update myblog set
title = #{title},
autor = #{autor},
where id = #{id}
+ 我们执行一下试试 - ![image-20210731163729668.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/3c96d405939120306fdb365eecca1cccd1bfc5eb40838a8439e28d7998847903) - ![image-20210731163748574.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/765b99d9a85a7b15339714be82c8f8f501d08837549436aa4a6fadf13918c6e4) + 更新成功,原来`set`标签和`where`标签一样,这么智能呀! + `set`标签可以自动帮我识别语句结尾的逗号并进行一定的处理! + 这样我们写的sql语句是不是更加具有灵活性呢?十分方便好用!

②、trim 标签

  • 通过上面的例子我们知道了,whereset 标签可以去除逗号 、and 、or 这种连接符。
  • trim 标签也是可以做到的!
  • xml
+ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
java复制代码    <insert id="insertBlogName" parameterType="map">
insert into myblog
(
<if test="title!=null and title!=''">
title,
</if>
<if test="autor!=null and autor!=''">
autor,
</if>
<if test="reads!=null and reads!=''">
`reads`,
</if>
<if test="creat_time!=null">
`creat_time`
</if>
)
values(
<if test="title!=null and title!=''">
#{title},
</if>
<if test="autor!=null and autor!=''">
#{autor},
</if>
<if test="reads!=null and reads!=''">
#{reads},
</if>
<if test="creat_time!=null">
#{creat_time}
</if>
)
</insert>
  • mapper
+ 
1
java复制代码Integer insertBlogName(Map<String,Object> map);
  • Junit Test
+ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码    @Test
public void insetBlogInfo(){
SqlSession session = MybatisUtils.getSession();
MyBlogMapper mapper = session.getMapper(MyBlogMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("title","如何榜上富婆?");
map.put("autor","大鱼");
map.put("reads","1000");
map.put("creat_time",new Date());
Integer num = mapper.insertBlogName(map);
System.out.println("一共新增了:"+num+"条数据");
session.commit();
session.close();
}
  • 执行结果
+ ![image-20210731194154983.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/3349016cc5b7a8b07113787ff8a3fde8260bff53c0dfc1b9fe1fcf3e83bcd5b3)
+ ![image-20210731194211831.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/b39833b038cacda0e52c74a33ec3e00d42e3fa8b1d5ae746712a1fa110ad2a57)
  • 记得session.commit(); 提交事务哦~
  • 这个sql如果我们不是传所有,是不是也会出现上面的问题?就是多个逗号,导致sql执行出错?
+ ![image-20210731194402341.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/bbba947b2015dbff94fa12998166480b576a8e35a52302b8f586184f5148c656)

image-20210731194402341.png

  • 我们可以使用trim标签来完成自动去除逗号等连接符
  • xml改造
+ 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
java复制代码    <insert id="insertBlogName1" parameterType="map">
insert into myblog
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="title!=null and title!=''">
title,
</if>
<if test="autor!=null and autor!=''">
autor,
</if>
<if test="reads!=null and reads!=''">
`reads`,
</if>
<if test="creat_time!=null">
`creat_time`
</if>
</trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
<if test="title!=null and title!=''">
#{title},
</if>
<if test="autor!=null and autor!=''">
#{autor},
</if>
<if test="reads!=null and reads!=''">
#{reads},
</if>
<if test="creat_time!=null">
#{creat_time}
</if>
</trim>
</insert>
+ 执行结果 - ![image-20210731194922870.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/18e53b37af3d3e8889a2b1d58e664810da3c5c37c18ad906c7eb0588279229d4) - ![image-20210731194938661.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/790bc7e177ead7c890db89bba5e7616bdb979efc38b82497b979d3242704803a) + 完美解决问题! + 重点解析 - prefix:开头所需加的 - suffix:结束所需加的 - suffixOverrides:每行语句结束时需要加的东西 - prefixOverrides:每行语句开始时需要加的东西 + 注意点 - 当数据库中设置了时间为datetime时,我们`if`标签中不要判断不为空 - 只需要判断不为null即可 - ![image-20210731195553294.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/5157ef9f09859a70c6b70d7eb7afc674e8a5d56f5184c987cadb6e91cd38811d)

四、foreach 标签

  • 在sql中写for循环是种什么感受?
  • 来玩一个需求:查询id为1、2、4、5的博客数据。
  • 学到这里了,千万不要这样用and这种的写法。
+ 
1
2
3
4
5
6
7
	java复制代码select * from myblog where id = 1 or id = 2 or id = 4 or id = 5

```#### ①、foreach简单用法
* xml


+
java复制代码 <select id="getBlogInfos" parameterType="map" resultType="Blog"> select * from myblog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>
1
2
3
4
* mapper


+
java复制代码List<Blog> getBlogInfos(Map<String,Object> map);
1
2
3
4
* Junit Test


+
java复制代码 @Test public void getBlogInfos(){ SqlSession session = MybatisUtils.getSession(); MyBlogMapper mapper = session.getMapper(MyBlogMapper.class); Map<String,Object> map = new HashMap<>(); List<String> ids = new ArrayList<>(); ids.add("1"); ids.add("2"); ids.add("4"); ids.add("5"); map.put("ids",ids); List<Blog> list = mapper.getBlogInfos(map); for (Blog blog : list) { System.out.println(blog); } session.close(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
* 执行结果


+ ![image-20210731221756853.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/543242261ad521fd0af091efc64b8f418ba7a22ebbd9a93e166f772093c2e18c)
+ foreach标签


- collection : 这个是我们在标签内所需遍历的集合,即放入map中的key
- item : 遍历出来的值我们所赋予的key
- open : 开始所加的参数
- close : 结束所加的参数
- separator : 每个值中间所加的参数#### ②、where in 配合foreach用法


+ xml


-
java复制代码 <select id="getBlogInfos1" parameterType="map" resultType="Blog"> select * from myblog <where> id in <foreach collection="ids" item="id" open="(" close=")" separator=","> #{id} </foreach> </where> </select>
1
2
3
4
5
+ 其他地方不需要变
+ 这个最终效果是


-
java复制代码select * from myblog WHERE id in ( ? , ? , ? , ? )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
		- ![image-20210731222613047.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/e8f651c243422e2bca8de17e68c07a13343acd90321c41324b0ff098a8bf8def)
+ 只列举这两个例子吧,具体怎么用还是得看具体的需求。
+ sql的长度是有限制的,这个ids里面的值太多会无法查询。
+ sql最大长度是:默认的SQL拼接长度最大值是2000个参数。


### 五、sql、include 标签


* 上面那么多标签玩下来,大家会发现一个问题,就是同一个业务中,可能会有很多重复的内容,比如每个查询都有`title`、`autor`等相同内容。
* 那么我们有没有类似于java中工具类的方法呢?
* 这时候就需要用到我们的sql、include 标签啦~
* xml


+
java复制代码 <sql id="if-key-info"> <if test="title!=null and title!=''"> title = #{title} </if> <if test="autor!=null and autor!=''"> and autor = #{autor} </if> <if test="reads!=null and reads!=''"> and `reads` = #{reads} </if> </sql> <select id="getBlogInfo" resultType="Blog" parameterType="map"> select * from myblog <where> <include refid="if-key-info"></include> </where> </select> ``` + 执行结果 - ![image-20210731223327323.png](https://gitee.com/songjianzaina/juejin_p14/raw/master/img/bbd17b91316aa19ecd48929aa1278ef34f5bcdae26987ede4a9cf37b81fd9b10) + 有了这个,我们可以把相同的的放在一起,是不是方便了很多呢? + sql id= "" :这里面的命名是随意的,只要在当前mapper.xml是唯一的即可 + include : 引用上面的命名即可!

路漫漫其修远兮,吾必将上下求索~

如果你认为i博主写的不错!写作不易,请点赞、关注、评论给博主一个鼓励吧~hahah

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%