开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
动态SQL环境搭建
什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
搭建环境
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
创建一个基础工程
-
导包
-
编写配置文件
mybatis-config.xml
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--是否开启驼峰命名自动映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <typeAlias type="com.isabella.pojo.Blog" alias="Blog"/> </typeAliases><mappers> <mapper class="com.isabella.dao.BlogMapper"></mapper> </mappers> -
编写实体类
Blog.java
package com.isabella.pojo; import java.util.Date; public class Blog { private String id; private String title; private String author; private Date createTime; private int views; public Blog() { } public Blog(String id, String title, String author, Date createTime, int views) { this.id = id; this.title = title; this.author = author; this.createTime = createTime; this.views = views; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public int getViews() { return views; } public void setViews(int views) { this.views = views; } @Override public String toString() { return "Blog{" + "id='" + id + ''' + ", title='" + title + ''' + ", author='" + author + ''' + ", createTime=" + createTime + ", views=" + views + '}'; } } -
编写实体类对应Mapper接口和Mapper.xml文件
BlogMapper.java:
package com.isabella.dao; import com.isabella.pojo.Blog; import java.util.List; import java.util.Map; public interface BlogMapper { //插入数据 int addBlog(Blog blog); //查询博客 List<Blog> querryBlogIF(Map map); } BlogMapper.xml:
<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.isabella.dao.BlogMapper"> <insert id="addBlog" parameterType="Blog"> insert into mybatis.blog (id, title, author, create_time, views) values (#{id},#{title},#{author},#{createTime},#{views}); </insert> <select id="querryBlogIF" parameterType="map" resultType="Blog"> select * from mybatis.blog where 1=1 <if test="title!=null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select> </mapper> -
编写测试类
MyTest.java:
import com.isabella.dao.BlogMapper; import com.isabella.pojo.Blog; import com.isabella.utils.IDutils; import com.isabella.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.Date; import java.util.HashMap; import java.util.List; public class MyTest { @Test public void addBlogTest() { SqlSession sqlsession = MybatisUtils.getSqlsession(); BlogMapper mapper = sqlsession.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDutils.getId()); blog.setTitle("Mybatis如此简单"); blog.setAuthor("Isabella"); blog.setCreateTime(new Date()); blog.setViews(9999); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("Java如此简单"); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("Spring如此简单"); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("微服务"); mapper.addBlog(blog); sqlsession.close(); } @Test public void queryBlogIF(){ SqlSession sqlsession = MybatisUtils.getSqlsession(); BlogMapper mapper = sqlsession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","Java如此简单"); List<Blog> blogs = mapper.querryBlogIF(map); for (Blog blog : blogs) { System.out.println(blog); } sqlsession.close(); } }
动态SQL常用标签
choose(when,otherwise)
BlogMapper.xml:
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from mybatis.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>
MyTest.java:
@Test
public void queryBlogChoose(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java如此简单");
map.put("views",9999);
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlsession.close();
}
trim(where,set)
where
BlogMapper.xml
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="title!=null">
title = #{title}
</if>
<if test="author!=null">
and author = #{author}
</if>
</where>
</select>
set
BlogMapper.java:
//更新博客
int updateBlog(Map map);
BlogMapper.xml:
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title!=null">
title = #{title},
</if>
<if test="author!=null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
所谓的动态SQL,本所还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
if
where,set,choose,when
动态SQL之Foreach
SQL片段
有的时候,我们可能会将一此公共的部分抽取出来,方复用
-
使用SQL标签抽取公共的部分
BlogMapper.xml:
<sql id="ifTitleAuthor"> <if test="title!=null"> title = #{title} </if> <if test="author!=null"> and author = #{author} </if> </sql> -
在需要使用的地方使用include标签引用即可
BlogMapper.xml:
<select id="queryBlogIF" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <include refid="ifTitleAuthor"></include> </where> </select>
注意事项:
- 最好 基于单表来定义SQL片段
- 不要存在where标签
foreach
select * from user where 1=1 and (id=1 or id=2 or id=3)
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
BlogMapper.java:
//查询第1-2-3号博客
List<Blog> queryBlogForeach(Map map);
BlogMapper.xml:
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
MyTest.java:
@Test
public void queryBlogForeach(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<String> ids = new ArrayList<String>();
ids.add("1");
ids.add("2");
ids.add("3");
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlsession.close();
}
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
建议:
- 先在Mysql中写出完整的SQL,再对应地去修改成为我们的动态SQL实现通用即可
缓存简介
查询 :连接数据库,耗资源
一次查询的结果,给它存在一个可以直接取到的地方-->内存:缓存
我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
-
什么是缓存[Cache]
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题
-
为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
-
什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
Mybatis缓存
-
Mybatis包含一个非常强大的查询特性,它可以非常方便地定制和配置缓存。缓存可以级大的提升查询效率
-
Mybatis系统中默认定义了两级缓存:一级缓存* *和二级缓存
- 默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称本地缓存)
- 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
-
一级缓存 也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如是需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
测试步骤:
-
开启日志
-
测试在一个Session中查询再次相同记录
-
查看日志输出
(user1==user2)=false的情况:
- 查询不同的东西
- 增删改操作,可能会改原来的数据,所以必定会刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存
sqlsession.clearCache();//手动清理缓存
小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段一级缓存就是一个Map
二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
-
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但我们想要的是会话关闭了,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
cache语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
步骤:
-
开启全局缓存(虽然默认开启,但显示地写出更利于开发)
<!--显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/> -
在要使用二级缓存的Mapper中开启
<!--在当前Mapper.xml中使用二级缓存--> <cache/>也可以自定义参数
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> -
测试
-
问题:我们现在要将实体类序列化,否则就会报错
Caused by: java.io.NotSerializableException: com.isabella.pojo.User
User.java:
public class User implements Serializable { //代码 }MyTest.java:
import com.isabella.dao.UserMapper; import com.isabella.pojo.User; import com.isabella.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; public class MyTest { @Test public void test(){ SqlSession sqlsession = MybatisUtils.getSqlsession(); SqlSession sqlsession2 = MybatisUtils.getSqlsession(); UserMapper mapper = sqlsession.getMapper(UserMapper.class); UserMapper mapper2 = sqlsession2.getMapper(UserMapper.class); User user1 = mapper.getUserById(1); System.out.println(user1); sqlsession.close(); User user2 = mapper2.getUserById(1); System.out.println(user1==user2); sqlsession2.close(); } } -
小结:
- 只要开启了二级缓存,在同一个Mapper就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会交到二级缓存中