五分钟带你玩转mybatis(六)面经必备,一级缓存和二级缓存全解析

举报
小鲍侃java 发表于 2021/09/09 23:01:50 2021/09/09
【摘要】 Mybatis的一级缓存和二级缓存是Mybatic自带的。 目的:将sql的查询结果存放于缓存内,在接下来再次查询时,会直接从缓存中取出而不是从数据库里面获取。这样会提升查询速度,但是会产生脏读。 注意:...

Mybatis的一级缓存和二级缓存是Mybatic自带的。
目的:将sql的查询结果存放于缓存内,在接下来再次查询时,会直接从缓存中取出而不是从数据库里面获取。这样会提升查询速度,但是会产生脏读。
注意:一级缓存是自动开启的,二级缓存是需要手动开启的。所以开启二级缓存需要慎重考虑。(使用spring boot环境)

一级缓存

一级缓存是自动开启的。是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层查询方法

	}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这样我们就可以查询sqls,spring boot中使用开启查看sql的功能.需要指定dao层的包的位置。经过测试发现两次相同的查询只会打印出一条sql.

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

  
 
  • 1



二级缓存

二级缓存是mapper级别的,只要是对一个mapper调用就可以使用二级缓存。
在Mybatis中查询的顺序是:二级缓存----一级缓存-------数据库。


使用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;
	}

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

但是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);
	}              
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

然后在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>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

最后需要序列化,要不会在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;
	}
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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


文章来源: baocl.blog.csdn.net,作者:小黄鸡1992,版权归原作者所有,如需转载,请联系作者。

原文链接:baocl.blog.csdn.net/article/details/82865096

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。