动态SQL
官方文档已经写的非常详细
动态Sql是指根据不同的条件生成不同的SQL语句
之前是手动拼接的,拼接sql语句是很麻烦的
四个标签:
- if
- choose(when,oherwise)
- trim(where,set)
- foreach
动态SQL环境搭建
- 数据库
- maven导包
- 实体类
- Mapper.xml
- 核心配置文件
- setting驼峰命名转换
- 工具类
- IDutils通过UUID实现随机ID
都是比较常用的套路
开始
-
创建数据库
-
Maven导包
-
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies>
-
-
编写POJO实体类
-
@Data @NoArgsConstructor @Alias("Blog") public class Blog { private String id; private String title; private String author; private Date createTime; //添加驼峰命名映射setting /*这个名字会映射成create_time*/ /*mapUnderscoreToCamelCase*/ private int views; }
-
-
编写接口以及接口映射
-
编写好实体类就写好对应的接口,接口写好写映射,然后把映射添加到核心配置文件中去
-
public interface blogMapper { @Select("select * from blog") List<Blog> getblogs(); int addBook(Blog blog); }
-
暂时写了两个接口,一个使用注解一个使用xml
-
<?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.haoyun.dao.blogMapper"> <insert id="addBook" parameterType="Blog"> insert into mybatis.blog (id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views}); </insert> </mapper>
-
因为添加了驼峰命名的设置,所以这里传递参数是填写的createTime
-
-
编写核心配置文件
-
使用的是外部配置文件
-
driver = com.mysql.cj.jdbc.Driver url= jdbc:mysql://localhost:3306/mybatis?useSSL=false -TRUE&useUnicode=true&characterEncoding=UTF-8 username =root password = 123456
-
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--引入外部配置文件--> <properties resource="db.properties"/> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <package name="com.haoyun.POJO"/> </typeAliases> <!--可以写多套环境配置,s复数,可以编写多套配置环境,但是default只能选择一套 --> <environments default="Test"> <!--这样就会选择Test的环境配置--> <environment id="Test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!--映射--> <mappers> <mapper class="com.haoyun.dao.blogMapper"/> <mapper resource="blogMapper.xml"/> </mappers> </configuration>
-
要注意setting标签编写的位置
-
设置 mapUnderscoreToCamelCase为true,名字不要打错
-
https://mybatis.org/mybatis-3/zh/configuration.html#settings
-
-
-
编写工具类
-
MybatisUtil这个工具类使用过好多次了,用来创建sqlsession
-
public class MybatisUtil { private static SqlSessionFactory sqlSessionFactory = null; static { InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream("mybatis-config.xml"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
再写一个随机生成ID的,使用UUID方法
-
public class IDutils { public static String getId(){ return UUID.randomUUID().toString().replaceAll("-",""); } }
-
将 “-” 替换为 “” 空
-
1a19b16580bb46588863d96fa4822d4f测试得到的id
-
-
环境整体结构
-
-
测试
-
public class blogTest { @Test public void Test() { SqlSession sqlSession = MybatisUtil.getSqlSession(); blogMapper mapper = sqlSession.getMapper(blogMapper.class); List<Blog> getblogs = mapper.getblogs(); for (Blog getblog : getblogs) { System.out.println(getblog); } sqlSession.close(); } @Test public void TestInsert() { SqlSession sqlSession = MybatisUtil.getSqlSession(); blogMapper mapper = sqlSession.getMapper(blogMapper.class); Blog blog = new Blog(); blog.setId(IDutils.getId()); blog.setTitle("TestTitle"); blog.setAuthor("TestAuthor"); //生成时间记得选择util包的Date blog.setCreateTime(new Date()); blog.setViews(1200); mapper.addBook(blog); sqlSession.commit(); sqlSession.close(); } @Test public void IDutilsTest() { System.out.println(IDutils.getId()); } }
-
-
动态SQL之IF语句
就是再Mapper.xml中添加if判断,实现sql的拼接,可以判断是否传递了对应列的参数来指定拼接对应的sql
流程:
-
添加接口方法
-
设置mapper.xml
- 加入if判断
-
测试
-
添加接口发方法
-
List<Blog> queryBlogIF(Map map);
-
-
设置mapper.xml
-
<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>
-
参数类型为map,返回值类型为Blog,这个添加了重命名
-
为了保证if条件都不符合,不进行拼接sql语句的情况,不能写成这样,这样拼接上是直接跟着and或or的,所以在if外要包上一层
-
select * from blog where
-
如果传递了参数,进行判断,然后拼接sql
-
-
测试
-
@Test public void queryBlogIFTest(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); blogMapper mapper = sqlSession.getMapper(blogMapper.class); HashMap hashMap = new HashMap(); hashMap.put("title","sadfasdf"); hashMap.put("author","l"); List<Blog> blogs = mapper.queryBlogIF(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
-
结果
-
-
可以选择传入一个空的hashMap,查询的就是全表的信息
-
trim(where,set)
trim 切除
这个主要的功能就是,拼接字符串,然后删除语句后缀的逗号
<select 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}
</select>
根据编写内容不同选择不同的标签,这里是更新,所以加入了set标签,使用方法也是一样的,只是这里拼接到where的时候会取除末尾的标签中语句的逗号,当然,两个if都没用上,会直接报错的,因为set之后直接拼接where,造成语法错误
@Test
public void queryBlogIFTest(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
blogMapper mapper = sqlSession.getMapper(blogMapper.class);
HashMap hashMap = new HashMap();
hashMap.put("title","sadsdf");
hashMap.put("author","test");
hashMap.put("id","1");
mapper.updateBlog(hashMap);
sqlSession.commit();
sqlSession.close();
}
最后author末尾的逗号是被消除了
choose(when,otherwise )
choose 选择
when 什么情况下
otherwise 否则
规则有点像switch,只去匹配case中的一个值,唯一不同的就是otherwise必须添加参数,如果走了where就不会走otherwise,where都没走就会走otherwise,执行顺序是从上往下执行的,先判定上面的where
choose语句
<select id="queryBlogChoose" 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 views = #{views}
</otherwise>
</choose>
</where>
</select>
多种情况测试
-
不填参数
-
@Test public void queryBlogIFTest(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); blogMapper mapper = sqlSession.getMapper(blogMapper.class); HashMap hashMap = new HashMap(); //hashMap.put("title","sadfasdf"); //hashMap.put("author","l"); //hashMap.put("views","55"); List<Blog> blogs = mapper.queryBlogChoose(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
-
-
自动的添加了views条件但是parameter为null
-
没查询出来
-
hashMap.put("views",10);
-
-
所以前面的when没有参数就会添加otherwise中的语句
-
加入中间when值
-
hashMap.put("author","l"); hashMap.put("views",10);
-
加入了中间的author when判断和otherwise判断
-
-
但是只执行了when的语句,otherwise并没有执行
-
when优先级
-
hashMap.put("title","sadfasdf"); hashMap.put("author","l"); hashMap.put("views",10);
-
三条都加入了
-
-
但是只执行了第一条
-
所以第一条的优先级最高
总结:
- 什么参数都不加的情况下会选择otherwise中的语句
- 第一个多条when条件,从上往下判断,执行排在上面的
- 只选择一条,包括otherwise
共同的父亲trim
如果前置有set关键字,就会删除无关的逗号,prefix 前缀
指定的是一个where,如果存在就会删除指定的内容
这是定制化sql用的一些东西
SQL片段
有一些重复的语句,可以提取出代码片段,实现sql语句的复用
<sql id="if_title_author">
<if test="title!= null">
and title = #{title}
</if>
<if test="author != null">
and author =#{author}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog
<where>
<include refid="if_title_author"/>
</where>
</select>
最好sql片段不要编写太复杂的事情,要不然重用效率就变低了
不要存在where标签,where是要根据条件语句来查询的加入就不好实现复用
foreach
<select id="queryBlogForEach" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = ${id}
</foreach>
</where>
</select>
传入一个map类型参数,返回值是一个Blog类型
collection 传递进一个集合,集合的名称为ids,那么用map传递参数的时候就要对应ids名进行传参
里面的每个元素是id,传递了集合就会进行自动的遍历集合,传递参数进id中,
open开始,加入where是为了解决select * from where 1=1 后面必须跟指定条件成立的语句,要不然动态sql没有进入foreach就会变成 select * from where 导致语句错误,加入where标签,还能根据判断去除不必要的and或or
这里就是open开始,填装了一个and (,注意,and和( 之间是有一个空格的,要不然where会识别不出来
在日志中查看得出的结果就是这样
本来用来连接其他条件的语句,不连接其他条件,and没有被去除,导致语句出错
close foreach结束时添加
separator 分隔符,用来分隔集合中元素的分隔符
最后给集合元素传参
@Test
public void queryBlogForEachTest(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
blogMapper mapper = sqlSession.getMapper(blogMapper.class);
HashMap hashMap = new HashMap();
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
hashMap.put("ids",arrayList);
List<Blog> blogs = mapper.queryBlogForEach(hashMap);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.commit();
sqlSession.close();
}
总结:
动态sql,本质还是sql,只是在sql层面,去执行一个逻辑代码,按照sql格式进行拼接,排列组合,去除不必要的字符
先在Mysql中写出完整的sql,再去修改实现动态sql,实现通用
多去看官方文档
怎么使用普通的sql查询还是不够的,需要多去了解更深层次的知识
- mysql引擎
- InnoDB底层原理
- 索引
- 索引优化