MyBatis的缓存

2023-12-27 14:39:51

1. 缓存介绍

缓存就是将我们经常查询的数据的结果保存到一个内存中(缓存就是内存中的一个对象),那么在下一次查询的时候就不用到数据库文件中查询,而是从内存中获取,从而减少与数据库的交付次数提高了响应速度。

假如有一条数据的查询量非常大,且内容基本不变,反复查询就会让数据库压力变大,这时我们就可以将数据存在内存缓存中,这样就大大提高的了查询效率,同时缓解了数据库压力。

为什么使用缓存?

首次访问时,查询数据库,并将数据存储到内存中;再次访问时直接访问缓存,减少IO、硬盘读写次数、提高效率

2. 一级缓存

一级缓存范围:SqlSession

开启方式:默认开启

执行缓存条件:同一个sqlsession执行同一条sql

不执行缓存:不同sqlSession或两次查询之间执行了增删改操作

2.1 一级缓存mapper

public interface UserMapper {
    User getUserById(Integer id);

    void deleteUserById(Integer id);
}
<mapper namespace="com.by.mapper.UserMapper">
    <select id="getUserById" parameterType="int" resultType="com.by.pojo.User">
        select * from user where id=#{id}
    </select>

    <delete id="deleteUserById" parameterType="int">
        delete from user where id=#{id}
    </delete>
</mapper>

2.2 一级缓存测试

从缓存中读取数据的情况:

@Test
    public void testGoCache(){
        SqlSession sqlSession = sessionFactory.openSession();
        //拥有同一个sqlsession
        UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);

        System.out.println("=============第一次查询============");
        User user1 = userMapper1.getUserById(41); //执行查询
        System.out.println(user1);

        System.out.println("=============第二次查询============");
        User user2 = userMapper2.getUserById(41);//执行查询?
        System.out.println(user2);
    }

在同一个SqlSession下同一条sql语句执行两次,可以看到第一次查询是从数据库中读取数据,第二次查询不查询数据库,从缓存中读取数据。

不从缓存中读取数据的情况:

情况一:拥有不同的SqlSession。

@Test
    public void testNoGoCache(){
        SqlSession sqlSession1 = sessionFactory.openSession();
        SqlSession sqlSession2 = sessionFactory.openSession();
        //拥有不同的sqlsession
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        System.out.println("=============第一次查询============");
        User user1 = userMapper1.getUserById(41); //执行查询
        System.out.println(user1);

        System.out.println("=============第二次查询============");
        User user2 = userMapper2.getUserById(41);//执行查询?不执行
        System.out.println(user2);
    }

在SqlSession不同的情况下,执行同一条sql语句我们可以看到两次查询均需要从数据中去读取数据。

情况二:拥有相同的sqlsession但两次查询之间执行增删改

@Test
    public void testNoGoCache2(){
        SqlSession sqlSession = sessionFactory.openSession();
        //拥有同一个sqlsession
        UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);

        System.out.println("=============第一次查询============");
        User user1 = userMapper1.getUserById(41); //执行查询
        System.out.println(user1);

        System.out.println("=============两次查询之间执行增删改=============");
        userMapper1.deleteUserById(1);
        sqlSession.commit();

        System.out.println("=============第二次查询============");
        User user2 = userMapper2.getUserById(41);//执行查询
        System.out.println(user2);
    }

当我们在两条sql查询语句中插入一条删除语句进行commit提交时,我们可以看到,第二次查询依旧不能从缓存中给中读取数据,需要从数据库中读取。

2.3 一级缓存的分析

一级缓存是SqlSession范围的缓存,当调用SqlSession的commit(),close()等方法时,就会清空一级缓存。

  1. 第一次发起查询用户id为 1 的用户信息,先去找缓存中是否有id为 1 的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。

  2. 如果sqlSession去执行 commit操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读

  3. 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

?3. 二级缓存

二级缓存范围:SqlSessionFactory

开启方式:<cashe></cache>?

执行缓存条件:同一个sqlSessionFactrory,sqlsession执行commit或close

不执行缓存:不同sqlSessionFactrory 或 两次查询之间执行了增删改

3.1 二级缓存的开启

二级缓存的开启有两种方式,可以全局开启,也可以局部开启

全局:在SqlMapConfig.xml 文件开启二级缓存

<settings>
    <!-- 开启二级缓存的支持 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

局部:配置相关的Mapper映射文件

<mapper namespace="com.by.dao.UserDao">
    <!-- 开启二级缓存的支持 -->
    <cache></cache>
<mapper namespace="com.by.mapper.UserMapper">
    <!--局部开启二级缓存-->
    <cache></cache>
    <select id="getUserById" parameterType="int" resultType="com.by.pojo.User">
        select * from user where id=#{id}
    </select>

    <delete id="deleteUserById" parameterType="int">
        delete from user where id=#{id}
    </delete>
</mapper>

3.2 二级缓存测试

从缓存中读取数据的情况:

@Test
    public void testGoCache(){
        SqlSession sqlSession1 = sessionFactory.openSession();
        SqlSession sqlSession2 = sessionFactory.openSession();
        //拥有相同的sqlSessionFactrory
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        System.out.println("=============第一次查询============");
        User user1 = userMapper1.getUserById(41); //执行sql
        System.out.println(user1);
        sqlSession1.commit(); //第一次查询session执行commit或close,才会把数据写到二级缓存

        System.out.println("=============第二次查询============");
        User user2 = userMapper2.getUserById(41);//执行sql?不执行sql
        System.out.println(user2);
    }

拥有不同的sqlSession相同的sqlSessionFactrory,第一次查询session后进行commit(或者close),这是数据已经被写到二级缓存中,当二次查询时不需要从数据库中读取数据,直接从缓存中读取数据。

不从缓存中读取数据的情况:

情况一:拥有不同的sqlSessionFactrory

@Test
    public void testNoGoCache() throws IOException {
        //加载mybatis-config.xml
        String resource = "mybatis-config.xml";

        InputStream inputStream1 = Resources.getResourceAsStream(resource);
        SqlSessionFactory sessionFactory1 = new SqlSessionFactoryBuilder().build(inputStream1);

        InputStream inputStream2 = Resources.getResourceAsStream(resource);
        SqlSessionFactory sessionFactory2 = new SqlSessionFactoryBuilder().build(inputStream2);

        SqlSession sqlSession1 = sessionFactory1.openSession();
        SqlSession sqlSession2 = sessionFactory2.openSession();
        //拥有不相同的sqlSessionFactrory
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        System.out.println("=============第一次查询============");
        User user1 = userMapper1.getUserById(41); //执行sql
        System.out.println(user1);
        sqlSession1.commit(); //第一次查询session执行commit或close,才会把数据写到二级缓存

        System.out.println("=============第二次查询============");
        User user2 = userMapper2.getUserById(41);//执行sql?执行sql
        System.out.println(user2);
    }

?当我们sqlSessionFactrory不同时,第二次执行相同的sql查询需要从数据库中读取数据。

情况一:拥有相同的sqlSessionFactrory但两次查询之间执行增删改

@Test
    public void testNoGoCache2(){
        SqlSession sqlSession1 = sessionFactory.openSession();
        SqlSession sqlSession2 = sessionFactory.openSession();
        //拥有相同的sqlSessionFactrory
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        System.out.println("=============第一次查询============");
        User user1 = userMapper1.getUserById(41); //执行sql
        System.out.println(user1);
        sqlSession1.commit(); //第一次查询session执行commit或close,才会把数据写到二级缓存


        System.out.println("=============两次查询之间执行增删改查=============");
        userMapper1.deleteUserById(1);
        sqlSession1.commit();

        System.out.println("=============第二次查询============");
        User user2 = userMapper2.getUserById(41);//执行sql?执行sql
        System.out.println(user2);
    }

?拥有相同的sqlSessionFactrory但两次查询之间执行增删改,第二次查询时不能从缓存中读取数据。

?3.3 二级缓存的分析

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存结构图:

二级缓存是多个 SqlSession 共享的,其作用域是 mapper 的同一个 namespace,不同sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同,即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据,而不再从数据库查询,从而提高查询效率。

文章来源:https://blog.csdn.net/2301_78090811/article/details/135193029
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。