MyBatis基础五(动态SQL,缓存)

什么是动态SQL?

动态SQL就是指根据不同的条件生成不同的SQL语句。

动态SQL元素和JSTL或基于类似xml的文本处理器相似。

搭建环境

创建表:

sql 复制代码
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'创建时间',
`view` int(30) not null comment'浏览量'
)engine=innodb default charset=utf8

创建一个基础工程:

1、导包

XML 复制代码
<dependencies>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
    </dependencies>

2、编写实体类

java 复制代码
@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    //属性名与字段名不一致,可在setting中配置驼峰命名映射
    private Date createTime;
    private int view;
}

由于字段与属性不同,在核心配置文件中采用驼峰命名映射:

3、编写实体类对应的接口和xml文件

编写自动生成id工具类:

java 复制代码
//抑制警告
@SuppressWarnings("all")
public class IDutils {
    //生成随机id
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }
}

编写测试类:

java 复制代码
@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("张三");
        blog.setCreateTime(new Date());
        blog.setView(6666);
        mapper.addBlog(blog);

        blog.setId(IDutils.getId());
        blog.setTitle("Web基础");
        mapper.addBlog(blog);

        blog.setId(IDutils.getId());
        blog.setTitle("SQL基础");
        mapper.addBlog(blog);

        blog.setId(IDutils.getId());
        blog.setTitle("Java基础");
        mapper.addBlog(blog);
        sqlSession.close();
    }

IF语句

编写接口:

编写接口对应的xml文件:

测试:无参数

java 复制代码
 @Test
    public void qureyBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

有参数Title:

choose(when,otherwise)

有时我们不想应用到所有的条件语句,只想要其中一项,choose能够满足我们的需求,它有点像Java中的switch语句。它能够按照提供了title就根据title查找,提供了author就按照author查找,若两者都没有提供,就返回所有符号条件的Blog

编写接口与xml文件:本节使用的where,在下节所描述与解释。

测试:

查询view=6666的blog信息

传入title=Java基础,view=666进行查询

传入author='张三',title='Java基础',在xml中优先执行的是title=#{title}

trim(where,set)

where语句

select * from blog

where

and title like 'someTitle'

执行上述语句,如果仅仅第二个条件匹配,会执行失败。添加一个where标签,减少在if语句中出现where 1=1 的情况。

where元素只会在至少有一个子元素的条件返回SQL子句的情况下才去插入"where"子句,而且,若语句的开头为"and"或"or",where元素也会将它们去除。rgwhere元素没有按正常套路出牌,我们可以通过自定义trim元素来定制where元素的功能。

改写if语句中的案例,除去where 1=1,加上where标签。

改写如下:

测试:

set更新语句

set元素会动态前置set关键字,同时也会删掉无关的逗号,因为用了条件语句之后就很可能会在生成的sql语句的后面留下这些逗号。利用set元素调整上述语句。

测试:

更新author

只修改title,让set实现自动去除逗号功能。

trim定制元素功能

<trim prefix="where" prefixOverrides="and|or">

...

</trim>

上述:利用trim自定义where元素的功能,prefixOverrides属性会忽略通过管道分割的文本序列。它的作用是移除指定在prefixOverrides属性中的内容,并且插入prefix属性中的指定内容。

<trim prefix="set" suffixOverrides=",">

...

</trim>

上述:此处添加了前缀值set,删除了后缀文本逗号。

SQL片段

有的时候,我们会将一些功能的部分抽取出来,方便复用!

抽取IF语句中使用的例子。

1、使用SQL标签抽取公共的部分

XML 复制代码
<sql id="select_queryBlogIF_if">
            <if test="title!=null">
                title=#{title}
            </if>
            <if test="author!=null">
                and author=#{author}
            </if>
    </sql>

2、在需要使用的地方使用include标签引用即可

XML 复制代码
<select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <include refid="select_queryBlogIF_if"></include>
        </where>
    </select>

测试结果:

注意事项:

  • 最好基于单表来定义SQL片段。
  • 不要再片段中写入where标签。

foreach

foreach对一个集合进行遍历,通常在构建IN条件语句的时候。

编写接口:

编写接口的xml文件,以下拼接处的and可去掉

编写测试类:

该测试类中集合未传参数。得出结果:

参数参数1,2,3:

结果:

缓存

查询的时候,需要连接数据库,耗费资源。

一个查询的结果,给他暂存存在一个可以直接收取到的地方(内存)

我们再次查询相同的数据时候,直接走缓存,就不用走数据库了。

1、什么是缓存?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。(不经常查询但经常改变的数据不适合用缓存)

MyBatis缓存

MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

MyBatis系统中默认定义了两级缓存,一级缓存和二级缓存。

  • 默认情况 下,只有一级缓存开启 ,(SqlSession级别的缓存,也称为本地缓存
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存。

在MyBatis中存在一些策略,默认的清除策略是LRU

  • LRU:最近最少使用,移除最长时间不被使用的对象。
  • FIFO:先进先出,按对象进入缓存的顺序来移除它们。
  • SOFT:软引用,基于垃圾回收器状态和软引用规则移除对象
  • WEAK:弱引用,更积极的基于垃圾收集器状态和弱引用规则移除对象。

一级缓存

一级缓存也叫本地缓存

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,不用必须再去查询数据库

测试在一个Session中查询两次相同记录:

编写接口:

java 复制代码
public interface UserMapper {
    //根据id查询用户
    User queryUsersByID(@Param("id") int id);
}

编写接口的xml文件:

XML 复制代码
 <select id="queryUsersByID" parameterType="_int" resultType="user">
        select * from user where id=#{id}
    </select>

编写测试:执行两次查询,而且第二次查询语句与第一次一致。

java 复制代码
   @Test
    public void queryUsersByIDTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUsersByID(1);
        System.out.println(user);
        System.out.println("===============");
        User user2 = mapper.queryUsersByID(1);
        System.out.println(user2);
        System.out.println(user==user2);
        sqlSession.close();
    }

注意:

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

增加一个update修改语句,测试上述案例中的sql执行次数

编写接口:

编写接口的xml文件:

XML 复制代码
 <update id="updateUser" parameterType="user">
        update user set name=#{name},pwd=#{pwd} where id=#{id}
    </update>

测试:

根据上述结果,增删改操作后,可能会改变原来的数据,所以必定会刷新缓存。

手动清理缓存:

sqlSession.clearCache();

一级缓存默认开启,只在一次sqlSession中有效,也就是拿到连接到关闭连接这个区间中。

二级缓存

二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。

基于namespace级别的缓存,一个名称空间,对应一个二级缓存。

工作机制:

  • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
  • 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中。
  • 新的会话查询信息,就可以从二级缓存中获取内容。
  • 不同的mapper查出的数据会放在自己对应的缓存(map)中。

开局全局缓存:在全部配置中setting中设置全局地开启配置文件中的所有映射器已经配置的任何缓存。cacheEnabled,默认值是true,但建议显式的写出。

要开启二级缓存,只需要在sql映射文件中添加一行

<cache/>

也可定制配置:

<cache eviction="FIFO"

flushInterval="60000"

size="512"

readOnly="true"/>

创建一个FIFO缓存,每个60秒刷新,最多可以存储结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

在未开启二级缓存时,创建两个sqlSession查询相同的内容,会执行两次sql。

开启二级缓存,二级缓存是事务性的,这意味着,当SqlSession完成并提交时,或是完成并回滚,但没有执行flushCache=true的insert/delete/update语句时,缓存会获得更新。

缓存原理

对该原理进行测试:

相关推荐
张张张31216 分钟前
4.2学习总结 Java:list系列集合
java·学习
KATA~19 分钟前
解决MyBatis-Plus枚举映射错误:No enum constant问题
java·数据库·mybatis
xyliiiiiL35 分钟前
一文总结常见项目排查
java·服务器·数据库
shaoing37 分钟前
MySQL 错误 报错:Table ‘performance_schema.session_variables’ Doesn’t Exist
java·开发语言·数据库
腥臭腐朽的日子熠熠生辉1 小时前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之2 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码2 小时前
Spring Task 定时任务
java·前端·spring
俏布斯2 小时前
算法日常记录
java·算法·leetcode
27669582922 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿