五分钟带你玩转mybatis(六)面经必备,一级缓存和二级缓存全解析
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
- 点赞
- 收藏
- 关注作者
评论(0)