介绍:
-
什么是动态SQL:动态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
搭建环境
- 导入的插件还是和以前一样
- 数据库字段
drop table if exists blog;
CREATE TABLE `blog`
(
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` varchar(30) NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
- 实体类Blog.java
package pojo;
import java.util.Date;
import lombok.Data;
@Data
public class Blog {
private String id;
private String title;
private String author;
private String createTime; //注意:属性名和字段名不一致,下面在配置文件中加入一行解决
private int views;
}
- 工具类MybatisUtils.java也和以前一样
- db.properties 也和以前一样
- 配置文件mybatis-config.xml也和以前一样,只是改了下面的这些位置
<settings>
<!-- 开启驼峰命名,解决上面的字段名不一样的问题 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--下面的这个注册mapper也要改了-->
<mappers>
<package name="com.wu.mapper"/>
</mappers>
- BlogMapper.java(接口)
package mapper;
public interface BlogMapper {
}
- BlogMapper.xml(用来编写sql语句)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wu.mapper.BlogMapper">
</mapper>
if
1、编写接口类
//根据条件查询博客
List<Blog> queryBlogIf(Map map);
2、编写SQL语句
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
<!--select * from blog where title = #{title} -->
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog
where 1=1
<if test="title != null">
and title = #{title}
</if>
</select>
这个看上去没问题,如果又改了一种情况后:
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog
where
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
如果不满足第一个条件反而满足了第二个条件呢,它的执行语句会变成这样
select * from blog where and author = #{author}
这样的话,sql语句明显错误,多了一个and,所以改造一下上面的,用where标签来代替
- where
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
- where标签很智能,如果你下面的条件都不满足,它会自动去掉where标签
- where 元素只会在子元素返回任何内容的情况下才插入 “where” 子句。
- 而且,若子句的开头为 “and” 或 “or”,where 元素也会将它们去除。所以大多数情况下还是要用where标签
3、测试
@Test
//自己在数据库中添加数据后,多测几遍,还有上面的sql语句也可以改改
public void queryBlogIF() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("author", "某某某");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
choose (when, otherwise)
1、编写接口方法
List<Blog> queryBlogChoose(Map map);
2、编写SQL语句
<!--select * from blog .... -->
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from blog
<where>
<!-- choose 标签下的都可以不加and ,otherwise也可以不写,因为这个里面只会执行一个条件,可以想象成switch -->
<choose>
<when test="title!=null">
and title = #{title}
</when>
<when test="author!=null">
and author = #{author}
</when>
<!--不满足上面的条件才会执行它-->
<otherwise>
and views >= #{views}
</otherwise>
</choose>
</where>
</select>
3、测试
自己还是把上面的sql语句改改,把下面的测试改改,多测试几遍应该就差不多了
@Test
public void queryBlogChoose() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("views", 8000);
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
它有点像 Java 中的 switch 语句。它只会满足其中的一种条件,比如,第一个条件满足后,后面的条件就没管了
Set
1、编写接口方法
void updateBlog(Map map);
2、编写SQL语句
<!--update blog set ...... -->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null">
title = #{title},
</if>
<if test="author!=null">
author = #{author},
</if>
<if test="views!=null">
views = #{views},
</if>
</set>
where id = #{id}
</update>
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
3、测试
@Test
public void updateBlog() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("title", "Java不简单啊");
map.put("id", "1");
mapper.updateBlog(map);
sqlSession.close();
}
foreach
- foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符
- 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
1、编写接口方法
//我们需要查询 blog 表中 id 分别为1,2,3的博客信息
List<Blog> queryBlogForeach(Map map);
sql语句应该为:
select * from blog where id in (1,2,3);
或者
select * from blog where (id= 1 or id = 2 or id =3)
等等…
但是世界有这么简单就好了,下面用freach来实现上面的两个sql语句,记得对比下foreach里面的标签的含义
- 第一个sql ------ select * from flog where id in (1,2,3)
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from blog
where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
这个要对比测试来看
@Test
public void queryBlogForeach() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids", ids);
mapper.queryBlogForeach(map).forEach(System.out::println);
sqlSession.close();
}
- 第二个sql—select * from blog where (id= 1 or id = 2 or id =3)
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
测试代码 和上面的一样
所以到现在发现什么了吗?
- collection:指定输入对象中的集合属性 (ids)
- item:每次遍历生成的对象 (id)
- open:开始遍历时的拼接字符串 ("(")
- close:结束时拼接的字符串 (")")
- separator:遍历对象之间需要拼接的字符串 (“or”) 和 (",")
还有一个小知识点 :
sql片段
- 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
- 提取SQL片段:
<sql id="if-author">
<if test="title != null">
adn title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
- 引用SQL片段:
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
注意:
-
最好基于 单表来定义 sql 片段,提高片段的可重用性
-
在 sql 片段中不要包括 where
小结 :
其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。