mybatis的缓存机制

简介: mybatis的缓存机制

mybatis的缓存机制

一、缓存:缓存存储

1、现有的查询策略:


image.png

现有查询策略:每次查询都会链接访问数据库
存在的问题:每次都需要获取链接,释放连接资源,降低程序运行效率,解决方案使用缓存

2、使用缓存:


image.png

缓存:内存中的一块存储空间,用于存放多个用户反复查询数据,有了缓存之后
后续的查询数据都会直接从缓存中获取

3、使用缓存的好处:

减少每次使用链接的占用,提升查询效率,
提升程序的运行效率

4、使用缓存的缺点:

占用大量的内存资源(成本高)

5、缓存的机制:

以空间换时间(效率)

二、mybatis缓存

1、mybatis的缓存机制

(1)一级缓存 基于sqlSession的缓存【默认开启】

一级缓存:也称为本地缓存,用于保存用户在一次会话过程中查询的结果,用户一次会话中只能使用一个sqlSession,一级缓存是自动开启的,不允许关闭。

注意:查询语句最后都是关闭资源,如果不关闭资源数据会出现闪动


不关闭资源,第一次查询四条,第二次5条,SqlSession.close()关闭资源就是为了关闭sqlSession的缓存

一级缓存的特点:


1、查询第一次时,获取到数据写入一级缓存,再次查询时从缓存获取,不再执行sql语句
2、若当前SqlSession发生修改、增加、删除动作时,就会立即把当前缓存的所有数据清空
3、对于查询操作,只要SqlSession没有调用flush或者close方法,它就一直存在

注意:一次缓存造成查询数据结果闪动


image.png


(2)二级缓存:基于mapper的缓存(基于指定实体类的缓存)(末默认不开启)

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。(也可以说是,在同一namespace下,共享一块缓存空间,如果多个mapper (dao.xml)共享同一namesapce 则也共享一块缓存,二级缓存是跨sqlsession,多个sqlsession可以去二级缓存获取数据。即可以针对同一个dao接口或者同一个命名空间(namespace)创建多个SqlSession )


二级缓存特点:


1、只要发生增删改,就会将·同一命名空间(namespace)下的缓存清空
2、使用二级缓存实体类必须实现序列化,否则报错
3、使用查询语句,默认只写入一级缓存,只有调用close(),commit()方法,
   才会将数据提交到二级缓存,其他的sqlsession才能拿到,不再执行sql语句

2、二级缓存 开启手工设置

1、在Mybatis框架中声明 使用二级缓存 在mybatis-config.xml中配置

mybatis-config.xml中


mybatis-config.xml:
<!--    设置缓存-->
    <settings>
        <!--开启Mybatis二级缓存
             cacheEnabled:启动mybatis二级缓存   值:布尔值   false:不启用  【默认值】   true :开启
         -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

注意:



image.png

2、在需要进行缓存的操作Mapper中指定使用缓存 在mapper文件中声明

  <!--开启当前查询操作缓存-->
    <cache></cache>

3、要缓存的实体类必须实现序列化接口(实体类中)

public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Date birthday;

实体类实现序列化接口的原因:

数据的数据唤出:长时间不用的放到缓存文件里


image.png


数据的唤入:当再次使用时,



image.png

4、缓存测试

1、在mybatis工具类中重写一个执行sqlSession的方法:


作用:用于测试缓存,实际开发不使用


//mybatisUtil.java 工具类
public class MybatisUtil {
    /**
     * 线程绑定对象,保证是同一个对象
     */
    private static final ThreadLocal<SqlSession> t1 = new ThreadLocal<>();
    private static SqlSessionFactory factory;
    static {
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        factory = new SqlSessionFactoryBuilder().build(is);
    }
    /**
     * 创建SqlSession
     *
     * @return sqlSession
     */
    private static SqlSession openSession() {
        SqlSession sqlSession = t1.get();
        if (sqlSession == null) {
            sqlSession = factory.openSession();
            t1.set(sqlSession);
        }
        return sqlSession;
    }
    /**
     * 仅用于测试缓存  每次调用方法获取的都是全新的连接  实际开发不使用
     * @return sqlSession
     */
    public static SqlSession getSqlSession(){
        return factory.openSession();
    }
    /**
     * 调用功能方法
     *
     * @param clazz
     * @return Object
     */
public  static Object getMapper(Class clazz) {
        return openSession().getMapper(clazz);
    }
    /**
     * 关闭资源
     */
    public static void close() {
        if (openSession() != null) {
            openSession().close();
        }
        t1.remove();
    }
    /**
     * 提交事务,关闭资源
     */
    public static void commit() {
        //1、提交事务
        commit();
        //2、关闭资源
        close();
        t1.remove();
    }
    /**
     * 回滚事务,关闭资源
     */
    public static void rollback() {
        //1、提交事务
        openSession().rollback();
        //2、关闭资源
        close();
        t1.remove();
    }
}

工具类的sqlSession不开启线程绑定对象【这样每次查询都会创建一个sqlSession】

    /**
     * 仅用于测试缓存  每次调用方法获取的都是全新的连接  实际开发不使用
     * @return sqlSession
     */
    public static SqlSession getSqlSession(){
        return factory.openSession();
    }


image.png

2、测试


//注意:测试之前mybatis工具类调用的方法不能开启ThreadLocal线程绑定对象,否则调用的都是同一个sqlSession
//必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存区
@Test
    public void CacheTest() {
        //第一次id查询id为1的同学
        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        UserDao mapper = sqlSession1.getMapper(UserDao.class);
        User user = new User();
        user.setId(1);
        List<User> users = mapper.selectUserAll(user);
        System.out.println("第一次id查询id为1的同学:");
        for (User user1 : users) {
            System.out.println(user1);
        }
        //必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存
        MybatisUtil.close();
        //第二次id查询id为1的同学
        SqlSession sqlSession2 = MybatisUtil.getSqlSession();
        UserDao mapper2 = sqlSession1.getMapper(UserDao.class);
        User user2 = new User();
        user.setId(1);
        List<User> users2 = mapper.selectUserAll(user);
        System.out.println("第二次id查询id为1的同学:");
        for (User user3: users2) {
            System.out.println(user3);
        }
        //必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存
        MybatisUtil.close();
    }

3、注意事项:


1、注意:测试之前mybatis工具类调用的方法不能开启ThreadLocal线程绑定对象,
        否则调用的都是同一个sqlSession
2、**必须关闭链接,不关闭放在一级缓存里,关掉才会放在二级缓存区中** 

3、二级缓存执行机制

开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。


image.png


二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。

当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。


1、一级缓存与二级缓存调用顺序:

获取数据先去二级缓存获取,如果有得到,写入(更新)一级缓存**
若没有得到,再去一级缓存,如果仍没有,再去数据库**
优先级: 二级缓存--->一级缓存--->数据库**

三、缓存中的脏数据

1、出现脏数据的原因:

1、主要原因:
数据库增删改之后,缓存的数据和数据库中的数据不匹配
2、详细原因:
使用二级缓存,当有一个session发生修改(增删改)时,
将二级缓存清空了,然而另一个session缓存过以前的查询结果
(此时二级缓存没数据,去一级缓存却拿到了之前的),则可能产生脏数据。


image.png

如上图:


1、第一步sqlsession1 去查询id=1的数据,
  并写入了sqlsession1的一级缓存与共同的二级缓存
2、第二步sqlsession2 去查询id=1的数据,
   发现二级缓存有数据,就不执行sql,并将其写入sqlsession2的一级缓存
3、第三步sqlsession2执行**修改** id=1的数据,
   清空了二级缓存与sqlsession2的一级缓存
4、第四步sqlsession1 再去查询id=1的数据,二级缓存没有,
   但却从sqlsession1的一级缓存读到了数据,但这却是修改之前的,就产生了脏数据

2、脏数据解决方案

判断是否刷新缓存flushCache;在默认的设置的select语句是不会刷新缓存的,insert/update/delte会刷新缓存。


3、Mybatis脏数据解决:

根据是否控制事务来判断是否刷新(清空)缓存:


SqlSession.commit()
SqlSession.rollback()

注意:


1、Mybatis会在事务控制时清空缓存,


2、后续功能查询一定不要控制事务(清空缓存) ,


3、增删改一定要控制事务


相关文章
|
4天前
|
SQL 缓存 Java
Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件
Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件
|
2月前
|
XML 缓存 Java
MyBatis二级缓存解密:深入探究缓存机制与应用场景
MyBatis二级缓存解密:深入探究缓存机制与应用场景
200 2
MyBatis二级缓存解密:深入探究缓存机制与应用场景
|
2月前
|
存储 缓存 Java
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
|
10月前
|
缓存 Java 数据库连接
面试官:说一下 MyBatis 缓存机制?
面试官:说一下 MyBatis 缓存机制?
198 2
|
2月前
|
缓存 Java 数据库连接
Hibernate或MyBatis:ORM映射、缓存机制等知识讲解梳理
Hibernate或MyBatis:ORM映射、缓存机制等知识讲解梳理
63 0
|
XML 缓存 Java
MyBatis 缓存机制分析,MyBatis 真的有二级缓存?
前言 缓存主要用来提高查询效率。以计算机的 CPU 为例,CPU 具有三级缓存,性能依次降低,优先从一级缓存查询,一级缓存未命中时再从二级缓存查询,二级缓存未命中时再从三级缓存查询。
96 0
MyBatis 缓存机制分析,MyBatis 真的有二级缓存?
|
11月前
|
SQL 缓存 Java
【MyBatis】mybatis缓存机制
【MyBatis】mybatis缓存机制
|
11月前
|
SQL 存储 缓存
一文让你彻底搞懂Mybatis之缓存机制
一文让你彻底搞懂Mybatis之缓存机制
|
11月前
|
缓存 Java 关系型数据库
Mybatis的缓存机制
Mybatis的缓存机制
61 0
|
12月前
|
SQL 缓存 Java
【MyBatis】day03动态SQL和缓存机制
【MyBatis】day03动态SQL和缓存机制
83 0