MyBatis(五) MyBatis的缓存

       使用缓存,可以让前端请求更快地获取数据,且能避免频繁的数据库交互,通常听说的redis、memcached就是,那么,MyBatis同样也提供了查询缓存的特性给我们使用。

       MyBatis有两个级别的缓存:

    一级缓存

       也叫本地缓存,默认会启用而且不能控制,下面测试一下:

package cn.linjk.mybatistest.mapper;

import cn.linjk.mybatistest.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.junit.Assert;
import org.junit.Test;

public class CacheTest extends BaseMapperTest {
    @Test
    public void testLevelOneCache() {
        SqlSession sqlSession = getSqlSession();
        User user = null;
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            user = userMapper.selectById(1L);
            user.setName("New Name");
            // 注意:这里虽然创建了新的userTmp对象,但是,查询出的用户名仍然是`New Name`!,而并没有更新数据库操作!
            User userTmp = userMapper.selectById(1L);
            Assert.assertEquals("New Name", userTmp.getName());
            Assert.assertEquals(user, userTmp);
        }
        finally {
            sqlSession.close();
        }
        // 开启新session......
        sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User userNew = userMapper.selectById(1L);
            Assert.assertEquals("admin", userNew.getName());
        }
        finally {
            sqlSession.close();
        }
    }
}

       从上面可以发现,MyBatis的一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,MyBatis会把执行的方法和参数通过算法生成缓存的键值,如不需要使用一级缓存,可以在select加参数flushCache:

       

       但是这样会增加数据库的查询次数,要尽量避免这样处理

       另外,数据库的INSERT、UPDATE和DELETE操作都会清空一级缓存。 

    二级缓存

       二级缓存使用场景:

       1. 以查询为主的应用,只有尽可能少的增删改操作

        2. 大多数表以单表操作存在,即避免了脏数据

        3. 可以按业务划分对表进行分组时,如关联的表较少,可以通过参照缓存降低脏数据

        4. 出现脏数据对系统功能无影响

        5. 任何情况下,可在业务层使用可控制的缓存代替二级缓存

       MyBatis的二级缓存默认是打开的,也可以在mybatis-config.xml文件配置:

       

       MyBatis的二级缓存是和命名空间绑定的,即需要配置在Mapper.xml映射文件(只需要加cache标签即可)或Mapper.java接口中,在Mapper.xml中命名空间就是XML根节点mapper的namespace属性,在Mapper.java接口中命名空间就是接口的全限定名称。例如:

       

       - eviction: 收回策略,有LRU(最近最少使用的)、FIFO(先进先出)、SOFT(软引用)、WEAK(弱引用)

       - flushInterval: 刷新间隔,默认情况不设置,即没有刷新间隔,缓存仅在调用语句时刷新

       - size: 引用数目,默认为1024

       - readOnly: 只读,默认为false

       这里设置了一个FIFO缓存,它每隔60秒刷新一次,存储集合或对象的512个引用,而且返回的对象被认为是只读的(这样在不同线程中的调用者之间修改会导致冲突)。

       上面在UserMapper.xml配置了缓存,当调用所有select的查询方法时,二级缓存就会开始工作,如果readOnly为true,MyBatis会使用Map来存储缓存值,为false则使用org.apache.ibatis.cache.decorators.SerializedCache序列化缓存来保证通过缓存获取数据,因此User来还需要实现java.io.Serializable接口,现在看看测试代码:

       第一个session和一级缓存一样,当关闭第一个session时,查询数据会被保存到二级缓存,因此,在新的session会出现缓存命中率0.33333,因为,这是第三次执行这个查询,所以结果为1/3=0.33333,注意,这个sql如果配置了flushCache为true,则二级缓存也不会起作用。

       如上的二级缓存是基于Map实现的内存缓存,当需要缓存大量数据时,可以使用EhCache缓存框架或Redis缓存数据库来保存二级缓存的数据。现在来看主流的Redis方案。

       这里为了方便,直接安装win版的redis:

       

         

         按默认设置安装测试成功:

         

       1. 添加项目依赖

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-redis</artifactId>
    <version>1.0.0-beta2</version>
</dependency>

       2. 配置Redis

           src/main/resources目录下新增文件redis.properties:

           

       3. 修改UserMapper.xml的配置

            User类实现序列化接口:

            ---- 配置IDEA可以检测生成UUID

           然后双击类名--alt+Enter键即可:

           

           如下:

           

       4. 测试 

           还是运行二级缓存的单元测试,成功,这时看看redis缓存:

           

           注意,当再次运行这个单元测试时,会失败,注意到这句:

           

           这是因为Redis作为缓存服务器,当应用再次运行查询时使用缓存的数据,因为是可读写缓存,所以不是相同的对象。

           这样,可以实现分布式缓存,而MyBatis默认的二级缓存不行。

           注意:

           MyBatis中自带的这两级缓存与MyBatis以及整个应用是运行在同一个JVM中,共享同一块堆内存的,如果这两级缓存中的数据量比较大,则可能影响系统中其他功能的运行,所以当需要缓存大量的数据时,优先考虑使用Redis、Memcache等缓存产品。

     脏数据

       二级缓存虽然能提高应用效率,但使用不当会产生脏数据,前面说到,MyBatis的二级缓存是与命名空间绑定的,使用多表查询时,设计的多个表的CRUD操作通常不在一个映射文件(即命名空间)中,这样,当有数据变化时,多表查询的缓存未必会被清空,就会产生脏数据了。

       例如如下操作会产生脏数据:

       1. 查询用户角色

       2. 修改角色名

       3. 查询用户角色

       在第三步因为有缓存,所以,查询到的用户角色名还是原来的。

       针对这种情况,对于简单不复杂的表关联,可以用参照缓存来实现,即让几个会关联的ER表同时使用同一个二级缓存,因为用户有角色,所以,在UserMapper.xml参照RoleMapper.xml的二级缓存:

       

       这样,在上面的第3步的查询中,不会从Redis缓存而是直接查询数据库去获取最新的数据。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值