当前位置 博文首页 > 立志欲坚不欲锐,成功在久不在速度:【Mybatis系列】Mybatis缓存

    立志欲坚不欲锐,成功在久不在速度:【Mybatis系列】Mybatis缓存

    作者:[db:作者] 时间:2021-07-31 08:57

    在学习mybatis缓存之前我们先来看三个问题:

    • 什么是缓存:?

    ? ? ? ?存在内存的临时数据

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

    • 为什么要使用缓存?

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

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

    ? ? ? ? 经常查询,并且不经常修改的数据

    Mybatis缓存:

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

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

    ? ? ? ?默认情况下只有一级缓存开启,(sqlsession级别的缓存,也称为本地缓存)

    ? ? ? ?二级缓存需要手动开启和配置,是基于namespace级别的缓存

    ? ? ? ?为了提高扩展性,MyBatis定义了缓存接口Cache

    一级缓存:

    特点:

    1. ? 一级缓存也叫本地缓存:
    2. ·?与数据库同一次会话期间查询到的数据会放在本地缓存中。
    3. ·?以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

    //根据id查询用户
    User queryUserById(@Param("id") int id);
    <select id="queryUserById" resultType="user">
      select * from user where id = #{id}
    </select>
    @Test
    public void testQueryUserById(){
       SqlSession session = MybatisUtils.getSession();
    ---------------------------------------------------------------------------------
       UserMapper mapper = session.getMapper(UserMapper.class);
     
       User user = mapper.queryUserById(1);
       System.out.println(user);
       User user2 = mapper.queryUserById(1);
       System.out.println(user2);
       System.out.println(user==user2);
     --------------------------------------------------------------------
       session.close();
    }

    结果分析:

    我们可以看到是同一个sqlSession? 执行了两次相同的查询,但是通过日志我们只能看到执行了一条SQL语句,说明第一条是通过数据库查询的,第二条是通过缓存查询的

    一级缓存失效的四种情况:?

    1.sqlSession不同:

    @Test
    public void testQueryUserById(){
       SqlSession session = MybatisUtils.getSession();
       SqlSession session2 = MybatisUtils.getSession();
       UserMapper mapper = session.getMapper(UserMapper.class);
       UserMapper mapper2 = session2.getMapper(UserMapper.class);
     
       User user = mapper.queryUserById(1);
       System.out.println(user);
       User user2 = mapper2.queryUserById(1);
       System.out.println(user2);
       System.out.println(user==user2);
     
       session.close();
       session2.close();
    }

    结果:?发现发送了两条sql语句,每个sqlSession中的缓存相互独立

    2.sqlSession相同,查询条件不同

    @Test
    public void testQueryUserById(){
       SqlSession session = MybatisUtils.getSession();
       UserMapper mapper = session.getMapper(UserMapper.class);
       UserMapper mapper2 = session.getMapper(UserMapper.class);
     
       User user = mapper.queryUserById(1);
       System.out.println(user);
       User user2 = mapper2.queryUserById(2);
       System.out.println(user2);
       System.out.println(user==user2);
     
       session.close();
    }

    3.sqlSession相同,两次查询之间执行了增删改操作:?

    增加方法
    //修改用户
    int updateUser(Map map);
    编写SQL
    <update id="updateUser" parameterType="map">
      update user set name = #{name} where id = #{id}
    </update>
    测试
    @Test
    public void testQueryUserById(){
       SqlSession session = MybatisUtils.getSession();
       UserMapper mapper = session.getMapper(UserMapper.class);
     
       User user = mapper.queryUserById(1);
       System.out.println(user);
     
       HashMap map = new HashMap();
       map.put("name","kuangshen");
       map.put("id",4);
       mapper.updateUser(map);
     
       User user2 = mapper.queryUserById(1);
       System.out.println(user2);
     
       System.out.println(user==user2);
     
       session.close();
    }

    观察结果:查询在中间执行了增删改操作后,重新执行了

    结论:因为增删改操作可能会对当前数据产生影响

    4.sqlSession相同,手动清除一级缓存:

    @Test
    public void testQueryUserById(){
       SqlSession session = MybatisUtils.getSession();
       UserMapper mapper = session.getMapper(UserMapper.class);
     
       User user = mapper.queryUserById(1);
       System.out.println(user);
     
       session.clearCache();//手动清除缓存
     
       User user2 = mapper.queryUserById(1);
       System.out.println(user2);
     
       System.out.println(user==user2);
     
       session.close();
    }

    二级缓存:

    二级缓存又称为全局缓存,由于一级缓存的作用域太低,所以诞生了二级缓存,基于namespace级别的缓存,一个名称控件,对应一个二级缓存:

    工作机制:

    一个会话查询一条数据----->?这个数据被放在当前会话的一级缓存中------->如果当前会话关闭了------->这个会话的一级缓存就没了------->?这个时候一级缓存的数据就会被保存到二级缓存中,新的会话内容就可以从二级缓存中获取内容

    如何使用?

    在mybatis-config.xml中开启全局缓存

    <setting name="cacheEnabled" value="true"/>

    在每个mapper.xml配置中使用二级缓存:

    <cache/>

    代码测试:

    @Test
    public void testQueryUserById(){
       SqlSession session = MybatisUtils.getSession();
       SqlSession session2 = MybatisUtils.getSession();
     
       UserMapper mapper = session.getMapper(UserMapper.class);
       UserMapper mapper2 = session2.getMapper(UserMapper.class);
     
       User user = mapper.queryUserById(1);
       System.out.println(user);
       session.close();
     
       User user2 = mapper2.queryUserById(1);
       System.out.println(user2);
       System.out.println(user==user2);
     
       session2.close();
    }

    结论

    ·?只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据

    ·?查出的数据都会被默认先放在一级缓存中

    ·?只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

    缓存的原理图:

    cs