文章目录:
3.4.3 insert、update、delete操作会刷新缓存
4.2 dao接口、mapper文件、myabtis配置文件
1.写在前面
首先,我们来聊聊什么是缓存Cache?
说到数据库啊,无论是MySQL、Sql Server、Oracle、Redis等等,它们当中都是有缓存机制的,那么到底什么是缓存呢?
· 存在内存中的临时数据。
· 将用户经常查询的数据放在缓存(相当于从内存中拷贝了一块高速内存区,存放临时数据)中,用户去查询数据就不用从磁盘上(关系型数据库文件中)查询了,只
需要去缓存中查询有无即可,从而提高了查询效率,解决了高并发系统的性能问题。
那么问什么要使用缓存呢?
· 目的是为了减少与数据库的交互次数,减少系统开销,提高系统的执行效率。
什么样的数据可以使用缓存呢?
· 经常查询,并且不经常改变的数据。
2.关于MyBatis中的缓存
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存,缓存可以极大的提升查询效率。
我们也可以在官方文档中看到有关缓存的定义说明
在MyBatis框架中默认定义了两级缓存:一级缓存和二级缓存。
· 默认情况下,MyBatis只开启了一级缓存(SqlSession级别的缓存,也称为本地缓存)
· 二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
· 为了提高扩展性,MyBatis定义了缓存接口Cache,可以通过实现Cache接口来自定义二级缓存。
3.一级缓存案例详解
一级缓存也叫本地缓存:SqlSeesion。
· 与数据库同一次会话期间查询到的数据会放在本地缓存中(也就是同一个SqlSession对象)。
· 以后如果需要获取相同的数据,就可以直接在缓存中拿,没有必须再去查询数据库了。
· 一级缓存是默认开启的,只在一次SqlSession会话中有效,也就是从当前SqlSession拿到连接到close关闭连接这个区间段。
3.1 首先写一个实体Bean
头三个注解是lombok中的,用来自动生成get/set方法、toString、无参/有参构造方法、hashCode、equals这些方法。
pom文件中的依赖分别添加了:mybatis、mysql驱动、lombok、junit
package com.szh.mybatis.bean; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * */ @Data @NoArgsConstructor @AllArgsConstructor public class User { private Integer id; private String name; private Integer age; }
3.2 dao接口、对应的mapper映射文件
package com.szh.mybatis.mapper; import com.szh.mybatis.bean.User; import org.apache.ibatis.annotations.Param; /** * */ public interface UserMapper { /** * 根据id查询用户信息 * @param id * @return */ User selectUserById(@Param("uid") Integer id); /** * 修改某个用户的信息 * @param user * @return */ int updateUser(User user); }
<?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.szh.mybatis.mapper.UserMapper"> <!-- 使用insert、update、delete、select标签编写sql语句 --> <sql id="userInfo"> id, name, age </sql> <sql id="userTable"> user </sql> <select id="selectUserById" resultType="com.szh.mybatis.bean.User"> select <include refid="userInfo"/> from <include refid="userTable"/> where id=#{uid} </select> <update id="updateUser" parameterType="com.szh.mybatis.bean.User"> update <include refid="userTable"/> set name=#{name},age=#{age} where id=#{id} </update> </mapper>
3.3 mybatis配置文件、工具类
这个可以参考这篇文章,这两个代码是一样的。
https://blog.csdn.net/weixin_43823808/article/details/118111961
3.4 测试方法
@Test public void test01() { SqlSession session=MyBatisUtil.getSqlSession(); UserMapper userMapper=session.getMapper(UserMapper.class); User user = userMapper.selectUserById(1001); System.out.println(user); System.out.println("=============================="); User user1 = userMapper.selectUserById(1001); System.out.println(user); session.close(); }
可以看到我们new第一个对象时,它去数据库中查询了一次,执行了一次select语句;而我们new第二个对象时,这个时候,它就没有再进数据库,没有执行select语句,因为面对同一个SqlSeesion会话对象,第一次查询之后,这条数据就已经存在于缓存中了,所以第二次查询直接去缓存中拿就可以了。
@Test public void test02() { SqlSession session=MyBatisUtil.getSqlSession(); UserMapper userMapper=session.getMapper(UserMapper.class); User user = userMapper.selectUserById(1001); System.out.println(user); System.out.println("=============================="); User user1 = userMapper.selectUserById(1002); System.out.println(user); session.close(); }
这个肯定是执行两次select语句了,因为虽然是同一个SqlSession会话对象,但这查询的是两条不同的数据,所以自然会执行两次sql语句,可以理解为缓存中此时有1001、1002这两条数据了。
3.4.3 insert、update、delete操作会刷新缓存
@Test public void test03() { SqlSession session=MyBatisUtil.getSqlSession(); UserMapper userMapper=session.getMapper(UserMapper.class); User user = userMapper.selectUserById(1001); System.out.println(user); int rows=userMapper.updateUser(new User(1002,"小宋",21)); System.out.println("=============================="); User user1 = userMapper.selectUserById(1001); System.out.println(user); session.close(); }
这里虽然是面对同一个SqlSession会话对象,然后查询的也是同一个数据,但是中间执行了一次update修改操作,所以这时候会刷新缓存,当你第二次再查询1001数据时,缓存已经被刷新了,就没有1001这条数据了,所以两次查询都会去数据库中查找。
@Test public void test04() { SqlSession session=MyBatisUtil.getSqlSession(); UserMapper userMapper=session.getMapper(UserMapper.class); User user = userMapper.selectUserById(1001); System.out.println(user); session.clearCache();//手动清理缓存 System.out.println("=============================="); User user1 = userMapper.selectUserById(1001); System.out.println(user); session.close(); }
这个就不再解释了,因为你都手动刷新缓存了,所以select两次,肯定两次都会去数据库中查找,因为缓存被刷新了。
4.二级缓存案例讲解
二级缓存也叫全局缓存,因为之前一级缓存的作用域太低了,所以就诞生了二级缓存。
二级缓存是基于namespace级别的缓存,一个命名空间(namespace)对应一个二级缓存。
工作机制:
· 一个会话对象(SqlSession)查询一条数据,这个数据就会被存放在当前会话的一级缓存中。
· 如果当前会话关闭了,这个会话对应的一级缓存就没了;只有当前会话关闭或者提交了,一级缓存中的数据才会被保存到二级缓存中。那么此时新的会话查询数据,就可以从二级缓存中获取数据。
· 不同的mapper具有不同的namespace,查出的数据自然也会放在自己对应的缓存中。
· 只要开启了二级缓存,在同一个mapper下就有效。
4.1 开启二级缓存之后,实体类需要实现序列化
package com.szh.mybatis.bean; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serial; import java.io.Serializable; /** * */ @Data @NoArgsConstructor @AllArgsConstructor public class User implements Serializable { @Serial private static final long serialVersionUID = 8349440997686871823L; private Integer id; private String name; private Integer age; }
4.2 dao接口、mapper文件、myabtis配置文件
dao接口和上面一级缓存案例的一样。
mapper文件中添加了一个 <cache/> 标签,其他都一样。
<cache/>
mybatis配置文件中多了这一行。
<!--显式的开启缓存--> <setting name="cacheEnabled" value="true"/>
4.3 测试方法
先把mapper文件中的 <cache/> 标签注释掉。
@Test public void test05() { SqlSession session=MyBatisUtil.getSqlSession(); SqlSession session2=MyBatisUtil.getSqlSession(); UserMapper userMapper=session.getMapper(UserMapper.class); UserMapper userMapper2=session2.getMapper(UserMapper.class); User user = userMapper.selectUserById(1001); System.out.println(user); session.close(); System.out.println("=============================="); User user2 = userMapper2.selectUserById(1001); System.out.println(user2); session2.close(); }
可以看到,我们在测试方法中创建了两个会话对象(SqlSeesion),那么在第一个会话对象close关闭之后,一级缓存就没了,所以第二个会话对象肯定还会去数据库中查找数据啊。
4.3.2 二级缓存面对两个SqlSession会话对象
先把mapper文件中的 <cache/> 标签打开。测试方法和上面的一样。
我们来看下面的执行结果。
这个时候,我们开启了二级缓存,那就不一样了啊,你第一个会话对象select查询之后,执行close关闭了,此时二级缓存会发挥作用,它会将你一级缓存(对应第一个会话对象)中查询到的那个数据存放到二级缓存中,这个时候,第二个会话对象再执行select,就不用去数据库中查找了,因为这个数据已经存在在二级缓存中了,直接拿数据就可以了。所以自始至终,只用执行一次select语句。
5.MyBatis缓存原理
默认开启一级缓存,之后使用<cache/>开启二级缓存。测试。
@Test public void test06() { SqlSession session=MyBatisUtil.getSqlSession(); SqlSession session2=MyBatisUtil.getSqlSession(); UserMapper userMapper=session.getMapper(UserMapper.class); UserMapper userMapper2=session2.getMapper(UserMapper.class); //查询1001,此时数据库缓存中什么都没有,查询1001,直接去数据库中查找,执行sql语句 User user = userMapper.selectUserById(1001); System.out.println(user); session.close(); //关闭第一个会话对象,此时1001数据会被转移到二级缓存中 System.out.println("=============================="); //查询1001,先去二级缓存中查找,有1001,直接从二级缓存中拿,不用再去数据库中查找,自然不会执行sql语句 User user2=userMapper2.selectUserById(1001); System.out.println(user2); //查询1002,二级缓存中没有、一级缓存中也没有,所以直接去数据库中查找,执行sql语句 User user3=userMapper2.selectUserById(1002); System.out.println(user2); System.out.println(user3); //查询1002,先去二级缓存中查找,有1002,直接从二级缓存中拿,不用再去数据库中查找,自然不会执行sql语句 User user4=userMapper2.selectUserById(1002); System.out.println(user4); session2.close(); }