动态Sql
1.什么是动态sql:
- 就是根据不同的条件生成不同的SQL语句
- 利用动态SQL这一特性可以彻底摆脱这种痛苦
- 如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- 类型
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
2.搭建环境
-
建一个表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` datetime NOT NULL COMMENT '创建时间', `views` int(30) NOT NULL COMMENT '浏览量' ) ENGINE=InnoDB DEFAULT CHARSET=utf8
-
创建实体类
import lombok.Data; import java.util.Date; @Data public class Blog { private String id; private String title; private String author; private Date createTime;//属性名和字段名不一致 private int views; }
-
增加工具类
import java.util.UUID; public class IDUtil { public static String genId(){ //自动生成id return UUID.randomUUID().toString().replaceAll("-",""); } }
-
编写接口
public interface BlogMapper { }
-
编写xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.mapper.BlogMapper"> </mapper>
-
注册
<mappers> <mapper resource="com/mapper/BlogMapper.xml"/> </mappers>
-
解决字段名与属性名不一致问题
<settings> <!--是否开启驼峰命名自动映射,解决属性名与字段名不一致的问题 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
3.增加初始化数据
-
创建接口
int addBlog(Blog blog);
-
编写xml文件
<insert id="addBlog" parameterType="blog"> insert into blog (`id`,`title`,`author`,`createTime`,`views`) values (#{id},#{title},#{author},#{createTime},#{views}); </insert>
-
初始化
import com.pojo.Blog; import com.utils.IDUtil; import com.utils.MybatisUtil; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.Date; public class MyTest { @Test public void addBlog(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDUtil.genId()); blog.setTitle("Mybatis入门"); blog.setAuthor("张起灵"); blog.setCreateTime(new Date()); blog.setViews(9999); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("第一个Mybatis程序"); blog.setAuthor("二月红"); blog.setCreateTime(new Date()); blog.setViews(8888); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("Mybatis配置"); blog.setAuthor("半截李"); blog.setCreateTime(new Date()); blog.setViews(7777); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("Mybatis分页"); blog.setAuthor("陈皮阿四"); blog.setCreateTime(new Date()); blog.setViews(6666); mapper.addBlog(blog); blog.setId(IDUtil.genId()); blog.setTitle("Mybatis注解"); blog.setAuthor("吴老狗"); blog.setCreateTime(new Date()); blog.setViews(5555); mapper.addBlog(blog); sqlSession.close(); } }
4.if语句
-
创建接口
List<Blog> getByBolg(Map map);
-
编写xml文件
<select id="getByBlog" parameterType="map" resultType="blog"> select * from blog where 1=1 <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
-
测试
@Test public void getByBlog(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","Mybatis入门"); map.put("author","张起灵"); List<Blog> byBlog = mapper.getByBlog(map); for (Blog blog : byBlog) { System.out.println(blog); } sqlSession.close(); }
-
结果
Opening JDBC Connection Created connection 1475491159. ==> Preparing: select * from blog where 1=1 and title = ? and author = ? ==> Parameters: Mybatis入门(String), 张起灵(String) <== Columns: id, title, author, create_time, views <== Row: 8fbd8a758ab54a79a0527b4db1b7dc91, Mybatis入门, 张起灵, 2020-08-26 22:50:29.0, 9999 <== Total: 1 Blog(id=8fbd8a758ab54a79a0527b4db1b7dc91, title=Mybatis入门, author=张起灵, createTime=Wed Aug 26 22:50:29 CST 2020, views=9999) Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@57f23557] Returned connection 1475491159 to pool.
-
小结
- 动态sql的if语句提供了可选的查找文本功能。
- 如果不传入 “title”,那么所有的 BLOG 都会返回;
- 如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)
- 如果希望通过 “title” 和 “author” 两个参数进行可选搜索,只需要加入另一个条件即可
5.trim (where, set)语句
前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “1=1’” 设置成动态条件,当我们什么都不输入时查询会失败,所有就需要where语句
5.1、 where语句
-
xml文件wher语句
<select id="getByBlog" 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>
-
测试
@Test public void getByBlog(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); List<Blog> byBlog = mapper.getByBlog(map); for (Blog blog : byBlog) { System.out.println(blog); } sqlSession.close(); }
-
结果
Preparing: select * from blog ==> Parameters: <== Columns: id, title, author, create_time, views <== Row: 8fbd8a758ab54a79a0527b4db1b7dc91, Mybatis入门, 张起灵, 2020-08-26 22:50:29.0, 9999 <== Row: 52f65ba723884c428295c271f018bd06, 第一个Mybatis程序, 二月红, 2020-08-26 22:50:29.0, 8888 <== Row: a6ec79eafe4e4f8d8a2aa56511522bee, Mybatis配置, 半截李, 2020-08-26 22:50:29.0, 7777 <== Row: 8c11053ef8d84c0185b0997b9d3ba79f, Mybatis分页, 陈皮阿四, 2020-08-26 22:50:29.0, 6666 <== Row: 305a489143e44334ac26c9dcd2ec404e, Mybatis注解, 吴老狗, 2020-08-26 22:50:29.0, 5555 <== Total: 5 Blog(id=8fbd8a758ab54a79a0527b4db1b7dc91, title=Mybatis入门, author=张起灵, createTime=Wed Aug 26 22:50:29 CST 2020, views=9999) Blog(id=52f65ba723884c428295c271f018bd06, title=第一个Mybatis程序, author=二月红, createTime=Wed Aug 26 22:50:29 CST 2020, views=8888) Blog(id=a6ec79eafe4e4f8d8a2aa56511522bee, title=Mybatis配置, author=半截李, createTime=Wed Aug 26 22:50:29 CST 2020, views=7777) Blog(id=8c11053ef8d84c0185b0997b9d3ba79f, title=Mybatis分页, author=陈皮阿四, createTime=Wed Aug 26 22:50:29 CST 2020, views=6666) Blog(id=305a489143e44334ac26c9dcd2ec404e, title=Mybatis注解, author=吴老狗, createTime=Wed Aug 26 22:50:29 CST 2020, views=5555) Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69379752] Returned connection 1765250898 to pool.
-
小结:
-
由结果可以看出,当什么都不输入的是sql语句自动变成 select * from blog
-
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
-
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能,比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim>
-
prefixOverrides 属性会忽略通过管道符分隔的文本序列,上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容
-
5.2、set语句
-
接口
int updateBlog(Map map);
-
xml文件
<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>
-
测试
@Test public void updateBlog(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","Mybatis入门2"); //map.put("author","张起灵"); map.put("id","8fbd8a758ab54a79a0527b4db1b7dc91"); mapper.updateBlog(map); sqlSession.close(); }
-
结果
Opening JDBC Connection Created connection 1024429571. ==> Preparing: update blog SET title = ? where id = ? ==> Parameters: Mybatis入门2(String), 8fbd8a758ab54a79a0527b4db1b7dc91(String) <== Updates: 1 Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3d0f8e03] Returned connection 1024429571 to pool.
-
小结
-
用于动态更新语句的类似解决方案叫做 set。
-
set元素可以用于动态包含需要更新的列,忽略其它不更新的列
-
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:
<trim prefix="SET" suffixOverrides=","> ... </trim>
-
注意,我们覆盖了后缀值设置,并且自定义了前缀值。
-
6.choose (when, otherwise)语句
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
-
接口
List<Blog> getByBolg(Map map);
-
xml文件
<select id="getByBlog" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> and title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and 1=1 </otherwise> </choose> </where> </select>
-
测试
@Test public void getByBlog(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","Mybatis入门"); map.put("author","张起灵"); List<Blog> byBlog = mapper.getByBlog(map); for (Blog blog : byBlog) { System.out.println(blog); } sqlSession.close(); }
-
结果
Opening JDBC Connection Created connection 494586676. ==> Preparing: select * from blog WHERE title = ? ==> Parameters: Mybatis入门(String) <== Total: 0 Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1d7acb34] Returned connection 494586676 to pool.
-
小结
- 传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。
- 若两者都没有传入,就返回所有.
- 若两者都传入,找不到
7.sql片段
有时候,我们可能会将一些功能的部分抽取出来,方便复用!
-
使用SQL标签抽取公共的部分
<sql id="if-title-author"> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </sql>
-
在需要使用的地方使用include标签引用即可
<update id="updateBlog" parameterType="map"> update blog <set> <include refid="if-title-author"></include> </set> where id = #{id} </update>
-
注意事项:
- 最好基于单表来定义SQL片段
- 不要存在where标签
8.foreach语句
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候
-
接口
List<Blog> queryBYBlogForEach(Map map);
-
xml文件
<!-- foreach语句--> <select id="queryByBlogForEach" parameterType="map" resultType="blog"> select * from blog <where> <foreach collection="viewss" open="(" separator="or" close=")" item="views"> views = #{views} </foreach> </where> </select>
-
测试
@Test public void queryByBolgForEach(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); ArrayList<Integer> viewss = new ArrayList<>(); map.put("viewss",viewss); viewss.add(9999); viewss.add(8888); mapper.queryByBlogForEach(map); sqlSession.close(); }
-
结果
Opening JDBC Connection Created connection 1765250898. ==> Preparing: select * from blog WHERE ( views = ? or views = ? ) ==> Parameters: 9999(Integer), 8888(Integer) <== Columns: id, title, author, create_time, views <== Row: 8fbd8a758ab54a79a0527b4db1b7dc91, Mybatis入门, 张起灵, 2020-08-26 22:50:29.0, 9999 <== Row: 52f65ba723884c428295c271f018bd06, 第一个Mybatis程序, 二月红, 2020-08-26 22:50:29.0, 8888 <== Total: 2 Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69379752] Returned connection 1765250898 to pool.
9.总结
- 所谓的动态sql,本质还是sql语句,只是我们可以在sql层面,去执行一个逻辑代码
- 多态Sql就是在拼接sql语句,我们只要保证sql的正确性,按照sql的格式,去排列组合就可以了
- 建议:
- 先在mysql中写出完整的sql再对应的去修改成为我们的动态sqls实现通用即可