MyBatis(三)

因为内容太多了,所以将其拆分为以下内容

参考

https://www.bilibili.com/video/BV1NE411Q7Nx

https://mybatis.org/mybatis-3/zh/index.html

动态 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

数据库准备

1
2
3
4
5
6
7
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

实体类

1
2
3
4
5
6
7
8
9
10
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}

接口和对应的 Mapper.xml

。。。

mybatis 配置略

1
2
3
4
public static SqlSession getSqlSession() {
// true : 自动提交事务
return sqlSessionFactory.openSession(true);
}
1
2
3
4
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

mapUnderscoreToCamelCase:驼峰命名自动映射

If

1
2
3
4
5
6
7
8
9
<select id="QueryBlogIf" 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>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void queryBlogIf(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map=new HashMap<>();
map.put("title","Java");
map.put("author","老杨");

List<Blog> blogs = mapper.QueryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

trim、where、set

where

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="QueryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
and author=#{author}
</when>
<otherwise>
and views >#{view}
</otherwise>
</choose>
</where>
</select>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void queryBlogChoose(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map=new HashMap<>();
// map.put("title","Java");
map.put("author","老杨");
map.put("view",3000);

List<Blog> blogs = mapper.QueryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}

set

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

1
2
3
4
5
6
7
8
9
10
11
12
<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>

测试

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void updateBlog(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map=new HashMap<>();
map.put("id","c3e4fcff19c64a4b8503883b4b999091");
// map.put("title","C");
map.put("author","小杨");

mapper.updateBlog(map);
sqlSession.close();
}

trim

参考

sql片段

抽取公共部分,方便复用

1
2
3
4
5
6
7
8
9
10
11
12
13
<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 1=1
<include refid="if-title-author"></include>
</select>

foreach

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

官方示例

1
2
3
4
5
6
7
8
9
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

自己来

1
2
3
4
5
6
7
8
9
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select *
from blog
<where>
<foreach collection="ids" item="id" open="and (" separator="or" close=")">
id=#{id}
</foreach>
</where>
</select>

open 开始符,close 结束符,separator 分隔符

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map=new HashMap<>();
List<Integer> ids=new ArrayList<>();
// ids.add(1);
// ids.add(2);
map.put("ids",ids);

List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}

缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void queryBlogById(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

Blog blog1 = mapper.queryBlogById(1);
Blog blog2 = mapper.queryBlogById(1);

System.out.println(blog1==blog2);

sqlSession.close();
}

默认开启一级缓存,如下只会查询一次

1
2
3
4
5
6
7
8
9
10
Opening JDBC Connection
Created connection 555273695.
==> Preparing: select * from blog where id = ?
==> Parameters: 1(Integer)
<== Columns: id, title, author, create_time, views
<== Row: 1, Mybatis, 老杨, 2021-02-08 11:00:41, 9999
<== Total: 1
true
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2118cddf]
Returned connection 555273695 to pool.

使用

1、开启全局缓存

1
2
<!-- 开启全局缓存 -->
<setting name="cacheEnable" value="true"/>

2、在要使用的的 Mapper 中加上

1
<cache/>

可以自定义参数

1
2
3
4
5
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

3、测试

官网

参考

因为内容太多了,所以将其拆分为以下内容