mybatis 一级缓存和二级缓存简介

来源:互联网 发布:vb 播放器 编辑:程序博客网 时间:2024/05/16 15:49

正如大多数持久层框架一样,MyBatis 同样提供了一级缓存二级缓存的支持

  1. 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空

  2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace)并且可自定义存储源,如 Ehcache。作用域为namespance是指对该namespance对应的配置文件中所有的select操作结果都缓存,这样不同线程之间就可以共用二级缓存。

启动二级缓存:在mapper配置文件中:<cache readOnly="true"/>。

二级缓存可以设置返回的缓存对象策略:<cache readOnly="true">。当readOnly="true"时,表示二级缓存返回给所有调用者同一个缓存对象实例,调用者可以update获取的缓存实例,但是这样可能会造成其他调用者出现数据不一致的情况(因为所有调用者调用的是同一个实例)。当readOnly="false"时,返回给调用者的是二级缓存总缓存对象的拷贝,即不同调用者获取的是缓存对象不同的实例,这样调用者对各自的缓存对象的修改不会影响到其他的调用者,即是安全的,所以默认是readOnly="false";


  3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

     4. 一级缓存测试实例:

(实例1)

/**     * 测试mybatis一级缓存,即session作用域缓存     */    @Test    public void testSelect_sessionCache() {        String statement = "com.xpspeed.centralcms.persist.mybatis.mapper.CommandFileMapper.select";        SqlSession session = SqlSessionUtil.openSession();        CommandFile commandFile1 = session.selectOne(statement,4l);        CommandFile commandFile2 = session.selectOne(statement,4l);        //commandFile2是从一级缓存中返回的缓存对象,与commandFile1同一个对象实例。        Assert.assertEquals(commandFile1, commandFile2);        //session关闭时清空缓存        session.close();        //显示清空缓存//            session.clearCache();        session = SqlSessionUtil.openSession();        CommandFile commandFile3 = session.selectOne(statement,4l);        //查询时commandFile3,一级缓存已经被清空,commandFile3与commandFile1不是同一个对象实例        Assert.assertNotEquals(commandFile1, commandFile3);        CommandFile commandFile4 = session.selectOne(statement,5l);        //一级缓存中不存在id为5的缓存对象,所以commandFile4是从数据库中查找出的实例。        Assert.assertNotEquals(commandFile3, commandFile4);    }

实例2)

在实例1中将session.close()换为执行一个update语句,也会导致一级缓存清空。

/**     * 测试mybatis一级缓存,即session作用域缓存     */    @Test    public void testSelect_sessionCache2() {        String statement = "com.xpspeed.centralcms.persist.mybatis.mapper.CommandFileMapper.select";        SqlSession session = SqlSessionUtil.openSession();        CommandFile commandFile1 = session.selectOne(statement,4l);        //执行更新后清空缓存        commandFile1.setName("bigfile-uploader2");        session.update("com.xpspeed.centralcms.persist.mybatis.mapper.CommandFileMapper.update",commandFile1);        CommandFile commandFile2 = session.selectOne(statement,4l);        //查询commandFile2时,一级缓存已经被清空,commandFile2与commandFile1不是同一个对象实例        Assert.assertNotEquals(commandFile1, commandFile2);    }

5.二级缓存测试实例

在配置文件中启动该namespace的二级缓存:<cache readOnly="false"/>

示例1:

/**     * 测试mybatis二级缓存:nameSpace作用域     * <cache readOnly="false"/>     */    @Test    public void testSelect_nameSpaceCache() {        String statement = "com.xpspeed.centralcms.persist.mybatis.mapper.CommandFileMapper.select";        SqlSession sqlSession1 = SqlSessionUtil.openSession();        SqlSession sqlSession2 = SqlSessionUtil.openSession();        //使用二级缓存时,CommandFile类必须实现一个Serializable接口        CommandFile commandFile1 = sqlSession1.selectOne(statement, 4l);        //sqlSession1提交后才会把数据放到二级缓存中,sqlSession2才能使用二级缓存        sqlSession1.commit();        CommandFile commandFile2 = sqlSession2.selectOne(statement, 4l);        /**         * <cache readOnly="false"/>时,返回缓存对象的拷贝,所以返回的commandFile2 != commandFile1         * 这样一个调用者有修改这个自己的缓存对象时,其他调用者不受影响。         */       Assert.assertNotEquals(commandFile1, commandFile2);    }

示例2:二级缓存设置<cache readOnly="true"/>

/**     * 测试mybatis二级缓存:nameSpace作用域     * <cache readOnly="true"/>     */    @Test    public void testSelect_nameSpaceCache2() {        String statement = "com.xpspeed.centralcms.persist.mybatis.mapper.CommandFileMapper.select";        SqlSession sqlSession1 = SqlSessionUtil.openSession();        SqlSession sqlSession2 = SqlSessionUtil.openSession();        //使用二级缓存时,CommandFile类必须实现一个Serializable接口        CommandFile commandFile1 = sqlSession1.selectOne(statement, 4l);        //sqlSession1提交后才会把数据放到二级缓存中,sqlSession2才能使用二级缓存        sqlSession1.commit();        CommandFile commandFile2 = sqlSession2.selectOne(statement, 4l);        /**         * <cache readOnly="true"/>时,对所有调用者返回同一个缓存对象,即同一个实例。         * 这样一个调用者有修改这个缓存对象时,其他调用者可能就会出现数据不一致的问题。         */        Assert.assertEquals(commandFile1, commandFile2);    }

实例3:

/**     * 测试mybatis二级缓存:nameSpace作用域     * 执行update操作,会清空二级缓存     */    @Test    public void testSelect_nameSpaceCache3() {        String statement = "com.xpspeed.centralcms.persist.mybatis.mapper.CommandFileMapper.select";        SqlSession sqlSession1 = SqlSessionUtil.openSession();        SqlSession sqlSession2 = SqlSessionUtil.openSession();        SqlSession sqlSession3 = SqlSessionUtil.openSession();        //使用二级缓存时,CommandFile类必须实现一个Serializable接口        CommandFile commandFile1 = sqlSession1.selectOne(statement, 4l);        //sqlSession1提交后才会把数据放到二级缓存中,sqlSession2才能使用二级缓存        sqlSession1.commit();        CommandFile commandFile2 = sqlSession2.selectOne(statement, 4l);        /**         * <cache readOnly="true"/>时,对所有调用者返回同一个缓存对象,即同一个实例。         * 这样一个调用者有修改这个缓存对象时,其他调用者可能就会出现数据不一致的问题。         */        Assert.assertEquals(commandFile1, commandFile2);        commandFile2.setName("bigfile-uploader");        sqlSession2.update("com.xpspeed.centralcms.persist.mybatis.mapper.CommandFileMapper.update", commandFile2);//      执行commit()表示执行update操作,会清空二级缓存中的数据        sqlSession2.commit();        CommandFile commandFile3 = sqlSession3.selectOne(statement, 4l);        //查询时,二级缓存中没有缓存数据了        Assert.assertNotEquals(commandFile2, commandFile3);    }

0 0