Mybatis缓存机制

Mybatis一级缓存

mybatis的一级缓存是sqlsession级别的,默认开启

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

1.不同sqlsession
2.同一个sqlsession  但是执行的语句不同  (先前已经缓存的sql中没有和现在要执行的sql一样的)
2.1 如果调用的方法名不同,但是执行的sql语句相同,仍然会使得一级缓存失效
3.进行了一次写操作,会清空一级缓存
4.手动清除了缓存  sqlSession.clearCache();

Mybatis二级缓存

开启二级缓存:
1.配置文件中添加:  <setting name="cacheEnabled" value="true"/>
2.mapper映射文件中添加:     <cache></cache>
    需要使用二级缓存的mapper添加,因为二级缓存的存储范围是SqlSessionFactory级别的
    如果所有mapper都开启二级缓存会造成不必要的资源浪费
3.实体类实现 Serializable 接口
4.session执行sql语句后调用session.close()  将该session在这期间执行过的sql存储到二级缓存中,即SqlSessionFactory中。

使二级缓存失效的情况:

1.进行数据库的写操作完成。(即commit)

开启二级缓存后,用Mybatis进行数据库查询的过程:

查二级缓存,查到直接返回结果,没查到则查询一级缓存,如果一级缓存也没有,则查询数据库。

调用sqlsession.close()后,一级缓存中的数据会写入二级缓存。

缓冲关联标签

涉及到 两个不同的mapper(前提是这两个mapper都由同一个SqlSessionFactory生成),一个emp 和 dept的多表查询(用empMapper中的多表查询sql) ,这时进行deptMapper的写操作,然后再进行一次多表查询

不会得到最新的结果,二级缓冲是 mapper级别,不同mapper 不会互相干扰和使用

这种情况下,会造成 “脏数据”。

解决方案:

在 查询的mapper中 添加一个 标签 缓冲关联标签

测试代码:

package com.example.mapper;

import com.example.entity.Brand;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class TestCache {
    public final static String configName="SqlMapConfig.xml";
    /*
           mybatis的一级缓存是sqlsession级别的,默认开启
           使一级缓存失效的四种情况:
           1.不同sqlsession
           2.同一个sqlsession  但是执行的语句不同  (先前已经缓存的sql中没有和现在要执行的sql一样的)
           2.1 如果调用的方法名不同,但是执行的sql语句相同,仍然会使得一级缓存失效
           3.进行了一次写操作
           4.手动清除了缓存  sqlSession.clearCache();
     */
    @Test
    public void testCache1(){
        InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
        BrandMapper brandMapper1 = sqlSession.getMapper(BrandMapper.class);
        BrandMapper brandMapper2 = sqlSession.getMapper(BrandMapper.class);
        //同一个sqlsession 不同mapper   执行一次sql语句  说明一级缓存至少是sqlsession级别的
        brandMapper1.selectAll();
        brandMapper2.selectAll();
    }

    @Test
    public void testCache2(){
        InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession1 = sessionFactory.openSession(true);//开启自动提交
        SqlSession sqlSession2 = sessionFactory.openSession(true);//开启自动提交
        //不同session   执行了两次sql语句   结合testCache1  说明一级缓存是sqlsession级别的
        BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
        BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);
        brandMapper1.selectAll();
        brandMapper2.selectAll();
    }

    @Test
    public void testCache3(){
        InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
        BrandMapper brandMapper1 = sqlSession.getMapper(BrandMapper.class);
        BrandMapper brandMapper2 = sqlSession.getMapper(BrandMapper.class);
        //同一个sqlsession 不同mapper 但是中间有写操作
        brandMapper1.selectAll();
        Brand brand = new Brand();
        brand.setBrandName("悦刻3");
        brand.setCompanyName("尼古丁真");
        brand.setOrdered(66);
        brand.setDescription("电子烟假,尼古丁真");
        brand.setStatus(1);
        brandMapper1.addBrand(brand);
        brandMapper2.selectAll();
    }

    @Test
    public void testCache4(){
        InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
        BrandMapper brandMapper1 = sqlSession.getMapper(BrandMapper.class);
        BrandMapper brandMapper2 = sqlSession.getMapper(BrandMapper.class);
        //同一个sqlsession 不同mapper   手动清除了缓存
        brandMapper1.selectAll();
        sqlSession.clearCache();//清除缓存
        brandMapper2.selectAll();
        //结果是执行了两次sql语句
    }

    @Test
    public void testCache5(){
        InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sessionFactory.openSession(true);//开启自动提交
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        brandMapper.selectAll();
        brandMapper.selectAll2();//即使两个方法中的sql语句相同,但是方法名不同,一级缓存失效,还是执行了两条sql
    }

    @Test
    public void testCache6(){
        /*
            两个不同的session  其中session1执行两次查询所有操作,在第二次查询前,session插入一条数据
            由于一级缓存的存在,session的第二次查询不会查询数据库,从而得到了脏数据。

            开启二级缓存可以解决这个问题
         */
        InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession1 = sessionFactory.openSession(true);
        SqlSession sqlSession2 = sessionFactory.openSession(true);
        BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
        BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);

        List<Brand> brands1 = brandMapper1.selectAll();
        System.out.println(brands1.size());

        Brand brand = new Brand();
        brand.setBrandName("悦刻3");
        brand.setCompanyName("尼古丁真");
        brand.setOrdered(66);
        brand.setDescription("电子烟假,尼古丁真");
        brand.setStatus(1);
        brandMapper2.addBrand(brand);
        brandMapper2.selectAll();//session2执行了查询所有语句

        List<Brand> brands2 = brandMapper1.selectAll();
        System.out.println(brands2.size());
    }

    @Test
    public void testCache7(){
        /*
            开启二级缓存:
            1.配置文件中添加:  <setting name="cacheEnabled" value="true"/>
            2.mapper映射文件中添加:     <cache></cache>
                需要使用二级缓存的mapper添加,因为二级缓存的存储范围是SqlSessionFactory级别的
                如果所有mapper都开启二级缓存会造成不必要的资源浪费
            3.实体类实现 Serializable 接口
            4.session执行sql语句后调用session.close()  将该session在这期间执行过的sql存储到二级缓存中,即SqlSessionFactory中。
         */
        InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession1 = sessionFactory.openSession(true);
        SqlSession sqlSession2 = sessionFactory.openSession(true);
        BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
        BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);

        brandMapper1.selectAll();//0.0                       0/1
        brandMapper2.selectAll();//0.0 因为此时s1没有close      0/2

        sqlSession2.close();

        brandMapper1.selectAll();//0.33333   s2 close 保存了selectAll的结果在二级缓存   此时命中    Cache Hit Ratio= 1/3
        sqlSession1.close();


    }

    @Test
    public void testCache8(){
        /*
            修改表的操作发生后,二级缓存和一级缓存都会被清除
         */
            InputStream is = TestCache.class.getClassLoader().getResourceAsStream(configName);
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession1 = sessionFactory.openSession(true);
            SqlSession sqlSession2 = sessionFactory.openSession(true);
            SqlSession sqlSession3 = sessionFactory.openSession(true);
            BrandMapper brandMapper1 = sqlSession1.getMapper(BrandMapper.class);
            BrandMapper brandMapper2 = sqlSession2.getMapper(BrandMapper.class);
            BrandMapper brandMapper3 = sqlSession3.getMapper(BrandMapper.class);

            List<Brand> brands1 = brandMapper1.selectAll();  // 0.0  0/1
            System.out.println(brands1.size());

//        brandMapper2.selectAll();//session2执行了查询所有语句
            Brand brand = new Brand();
            brand.setBrandName("悦刻3");
            brand.setCompanyName("尼古丁真");
            brand.setOrdered(66);
            brand.setDescription("电子烟假,尼古丁真");
            brand.setStatus(1);
            brandMapper2.addBrand(brand); //在commit动作发生后,二级缓存和一级缓存被清空

            //二级缓存和一级缓存被清空,被清空后,甚至连访问二级缓存这个操作都不会发生,直接访问数据库,所以这里不是 0/2
            brandMapper2.selectAll();

            sqlSession2.close();

            /*
            Cache Hit Ratio [com.example.mapper.BrandMapper]: 0.5
            请注意 这里的Cache Hit Ratio是每次查询累加的命中率,并不是单次查询的命中率,也肯定不是这个。
            */
            brandMapper3.selectAll();//0.5   1/2
            brandMapper3.selectAll();//0.6666666666666666  2/3
            brandMapper3.selectAll();//0.75   3/4
            brandMapper3.selectAll();//0.8   4/5
            brandMapper3.selectAll();//0.8333333333333334    5/6
            sqlSession3.close();


    }


}

完整的测试项目:

MybatisProject01: 仅用于练习使用mybatis - Gitee.com

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值