请说说mybatis的一级缓存和二级缓存

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 请说说mybatis的一级缓存和二级缓存


image.png

在面试过程中经常会问请说说mybatis的一级缓存和二级缓存,下文将带你实现如何使用。如果不敢兴趣请直接看结论。

1.理解mybatis缓存

Mybatis的一级缓存和二级缓存是Mybatis自带的。

目的:将sql的查询结果存放于缓存内,在接下来再次查询时,会直接从缓存中取出而不是从数据库里面获取。这样会提升查询速度,但是会产生脏读。

注意:一级缓存是自动开启的,二级缓存是需要手动开启的。所以开启二级缓存需要慎重考虑。(使用spring boot环境)

2.一级缓存

一级缓存是自动开启的。是sqlSession级别的缓存。

个人理解就是同一个事务之内,调取两次相同sql。会读取一级缓存。

我们只要满足以下条件就可以使用一级缓存:

  • 1.sqlSession级别的缓存。
  • 2.使用@Transactional。(必须有此注解)

此处只贴上service的代码,其余的就是普通的mvc查询代码很简单。

       @Transactional
  public City selectCity(int id) {
      System.out.println("第一次查询");
      redisDao.selectAnno(id);          //调用dao层查询方法
      System.out.println("第二次查询");
    return redisDao.selectAnno(id);  //调用dao层查询方法
  }

这样我们就可以看到查询sql。经过测试发现两次相同的查询只会打印出一条sql。

spring boot中使用开启查看sql的功能,需要指定dao层的包的位置。

logging.level.com.example.redis.dao=debug

3.二级缓存

1.什么是二级缓存

二级缓存是mapper级别的,只要是对一个mapper调用就可以使用二级缓存。

2.查询顺序

在Mybatis中查询的顺序是:二级缓存----一级缓存-------数据库。

3.使用redis作二级缓存

首先连接redis,实现Cache 接口,实现接口中的方法,从redis取值和存值。这里使用了RedisTemplate 。

public class MybatisRedisCache implements Cache {
  private static Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);
  private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
  private RedisTemplate redisTemplate;
  private String id;
  private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间
  public MybatisRedisCache(String id) {
    if (id == null) {
      throw new IllegalArgumentException("Cache instances require an ID");
    }
    this.id = id;
  }
  @Override
  public void clear() {
    RedisTemplate redisTemplate = getRedisTemplate();
    redisTemplate.execute((RedisCallback) connection -> {
      connection.flushDb();
      return null;
    });
    log.debug("Clear all the cached query result from redis");
  }
  @Override
  public String getId() {
    // TODO Auto-generated method stub
    return this.id;
  }
  @Override
  public Object getObject(Object key) {
    RedisTemplate redisTemplate = getRedisTemplate();
    //ValueOperations opsForValue = redisTemplate.opsForValue();
    log.debug("Get cached query result from redis");
    //return opsForValue.get(key);
    Object obj = redisTemplate.opsForValue().get(key.toString());
        return obj;
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return readWriteLock;
  }
  @Override
  public int getSize() {
    return 0;
  }
  @Override
  @SuppressWarnings("uncheck")
  public void putObject(Object key, Object value) {
    //RedisTemplate redisTemplate = getRedisTemplate();
    //ValueOperations opsForValue = redisTemplate.opsForValue();
    //opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
        redisTemplate.opsForValue().set(key.toString(), value, 2, TimeUnit.DAYS);
    log.debug("Put query result to redis");
  }
  @Override
  public Object removeObject(Object key) {
    RedisTemplate redisTemplate = getRedisTemplate();
    redisTemplate.delete(key);
    log.debug("Remove cached query result from redis");
    return null;
  }
  private RedisTemplate getRedisTemplate() {
    if (redisTemplate == null) {
      redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
    }
    return redisTemplate;
  }
}

但是RedisTemplate 不是spring提供的,不能用spring的ioc进行依赖注入,所以需要我们手写来注册RedisTemplate 。

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
  private static ApplicationContext applicationContext;
  @Override
  public void setApplicationContext(ApplicationContext ctx) throws BeansException {
    applicationContext = ctx;
  }
  /**
   * Get application context from everywhere
   *
   * @return
   */
  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }
  /**
   * Get bean by class
   *
   * @param clazz
   * @param <T>
   * @return
   */
  public static <T> T getBean(Class<T> clazz) {
    return applicationContext.getBean(clazz);
  }
  /**
   * Get bean by class name
   *
   * @param name
   * @param <T>
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <T> T getBean(String name) {
    return (T) applicationContext.getBean(name);
  }              
}

然后在mapper文件中开启二级缓存。

<mapper namespace="com.example.redis.dao.RedisDao">
<!-- 开启二级缓存 -->
<cache type="com.example.redis.cache.MybatisRedisCache"></cache> 
  <select id="selectAnno" resultType="com.example.redis.entity.City">
    select * from city 
    <if test="id != null">where id = #{id}</if>
  </select>
  <insert id="insertIn">
    INSERT INTO `mysql`.`city` (`id`,
    `provinceId`, `cityName`, `description`) VALUES ('1', '1', 'aa', 'aa')
  </insert>
</mapper>

最后需要序列化,要不会在redis存放乱码。

@Configuration
public class RedisConfig {
  @Autowired
  private RedisTemplate redisTemplate;
  @Bean
  public RedisTemplate redisTemplateInit() {
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    // 设置序列化Key的实例化对象
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    // 设置序列化Value的实例化对象
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
    redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    return redisTemplate;
  }
}

然后我们测试,在不同的sqlsession中执行同一条sql时,只有第一次会打印出sql。我们在redis中会发现,执行一次sql后会在redis存储本次的查询结果。

4.结论

一级缓存:同一个事务之内,调取两次相同sql。会读取一级缓存。

二级缓存:mapper级别的,只要是对一个mapper调用就可以使用二级缓存。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
存储 缓存 芯片
让星星⭐月亮告诉你,当我们在说CPU一级缓存二级缓存三级缓存的时候,我们到底在说什么?
本文介绍了CPU缓存的基本概念和作用,以及不同级别的缓存(L1、L2、L3)的特点和工作原理。CPU缓存是CPU内部的存储器,用于存储RAM中的数据和指令副本,以提高数据访问速度,减少CPU与RAM之间的速度差异。L1缓存位于处理器内部,速度最快;L2缓存容量更大,但速度稍慢;L3缓存容量最大,由所有CPU内核共享。文章还对比了DRAM和SRAM两种内存类型,解释了它们在计算机系统中的应用。
77 1
|
2月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
17天前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
17天前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
52 5
|
1月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
5月前
|
SQL 缓存 Java
MYBATIS缓存
MYBATIS缓存
|
4月前
|
SQL 缓存 Java
【面试官】Mybatis缓存有什么问题吗?
面试官:你说下对MyBatis的理解?面试官:那SqlSession知道吧?面试官:Mybatis的缓存有哪几种?面试官:那Mybatis缓存有什么问题吗?面试官:Mybatis分页插件是怎么
【面试官】Mybatis缓存有什么问题吗?
|
4月前
|
缓存 算法 Java
关于MyBatis的缓存详解
MyBatis 的缓存机制非常灵活,可以通过简单的配置来满足不同的性能需求。合理地使用缓存可以显著提高应用程序的性能,尤其是在处理大量数据库查询时。然而,开发者需要注意缓存的一致性和并发问题,特别是在使用可读写缓存时。
|
3月前
|
存储 缓存 Java
|
5月前
|
缓存 NoSQL Java
在 SSM 架构(Spring + SpringMVC + MyBatis)中,可以通过 Spring 的注解式缓存来实现 Redis 缓存功能
【6月更文挑战第18天】在SSM(Spring+SpringMVC+MyBatis)中集成Redis缓存,涉及以下步骤:添加Spring Boot的`spring-boot-starter-data-redis`依赖;配置Redis连接池(如JedisPoolConfig)和连接工厂;在Service层使用`@Cacheable`注解标记缓存方法,指定缓存名和键生成策略;最后,在主配置类启用缓存注解。通过这些步骤,可以利用Spring的注解实现Redis缓存。
79 2

热门文章

最新文章

下一篇
无影云桌面