MyBatis
- MyBatis作为一个优秀的ORM框架,缓存是其必不可少的功能之一。
- 其本身具有缓存的功能,默认开启一级缓存,它的一级缓存是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,其对象中有一个内存区域用于存储缓存数据。但是不同的sqlSession之间的缓存区域却是相互不影响的。
- 其有内置的二级缓存,但是默认关闭。其实SqlSessionFactory级别的,作用域为mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句,第二次会从缓存中查询,而不从数据库中查询,大大提高了查询效率。
Redis做缓存的好处
- Redis支持数据的持久化
- 支持数据备份
- 性能极高-Redis能读的速度是110000次/s,写的速度是81000次/s
原理
- 在MyBatis的存储模块中,采用装饰器模式的变体。
结构如上图所示 - 在装饰器模式下,可扩展性强。
- 当有新功能添加时,我们只需要添加新的装饰器实现类,然后以组合方式添加这个新的装饰器即可。
- 如果我们要实现一个自己的二级缓存,我们只需要实现MyBatis的Cache接口。
实现
配置
<settings>
<!-- 启动二级缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- ... -->
</settings>
| |
缓存工具类
- 实现缓存的
取出
,存储
,清除
等。 - 实现pojo的序列化,这里采用的Alibaba的开源项目
fastjson
- 在这里使用的
Jedis
来操作Redis
/**
* @Author:Keyu
*/
@Component
public class RedisCacheUtil implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(RedisCacheUtil.class);
private JedisPool pool;
@Override
public void afterPropertiesSet() throws Exception {
pool = new JedisPool("redis://localhost:6379/3");
}
public void putCathe(Object key,Object value){
Jedis jedis = null;
try {
jedis = pool.getResource();
String json = JSONObject.toJSONString(value);
jedis.set(key.toString(),json);
logger.info("存储缓存 key:"+key);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public Object getCathe(Object key){
Jedis jedis = null;
try {
jedis = pool.getResource();
String json = jedis.get(key.toString());
//取得热点缓存数据
List<Comment> value = JSON.parseArray(json,Comment.class);
if (value == null){
logger.info("未命中 key:"+key);
}
else{
logger.info("命中 key:"+key);
}
return value;
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
} finally {
if (jedis != null) {
jedis.close();
}
}
return null;
}
public Object removeCathe(Object key){
Jedis jedis = null;
try {
jedis = pool.getResource();
return jedis.expire(key.toString(),0);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
} finally {
if (jedis != null) {
jedis.close();
}
}
return null;
}
public int getCatheSize(){
Jedis jedis = null;
try {
jedis = pool.getResource();
int size = Integer.valueOf(jedis.dbSize().toString());
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
} finally {
if (jedis != null) {
jedis.close();
}
}
return 0;
}
public void clearCathe(){
Jedis jedis = null;
try {
jedis = pool.getResource();
jedis.flushDB();
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
| |
实现Cache类
/**
* @Author:Keyu
*/
public class MyBatisRedisCache implements Cache {
private static RedisCacheUtil redisCacheUtil;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static final Logger logger = LoggerFactory.getLogger(MyBatisRedisCache.class);
private String id;
/**
* 这里需要静态注入工具类
* @param redisCacheUtil
*/
public static void setRedisCacheUtil(RedisCacheUtil redisCacheUtil){
MyBatisRedisCache.redisCacheUtil = redisCacheUtil;
}
public MyBatisRedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.debug(">>>>>>初始化 id:" + id);
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
logger.debug(">>>>>>putObejct key:"+key);
redisCacheUtil.putCathe(key,value);
}
@Override
public Object getObject(Object key) {
logger.debug("<<<<<<<<getObject key:"+key);
Object value = redisCacheUtil.getCathe(key);
return value;
}
@Override
public Object removeObject(Object key) {
logger.debug(">>>>>>>>>removeObject key:"+key);
return redisCacheUtil.removeCathe(key);
}
@Override
public void clear() {
logger.debug(">>>>>>>>>清空缓存");
redisCacheUtil.clearCathe();
}
@Override
public int getSize() {
return redisCacheUtil.getCatheSize();
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
| |
静态注入组件
/**
* @Author:Keyu
*/
@Component
public class MyBatisRedisCacheTransfer {
@Autowired
public void setRedisCatheUtil(RedisCacheUtil redisCatheUtil){
MyBatisRedisCache.setRedisCacheUtil(redisCatheUtil);
}
}
| |
使用
- 在需要使用二级缓存的Mapper下声明刚刚定义好的缓存类即可
/**
* @Author:Keyu
*/
@Mapper
@CacheNamespace(implementation = site.keyu.askme.cache.MyBatisRedisCache.class)
public interface CommentDao {
//TODO
}
| |
测试
第一次请求
- 未命中缓存并且存储缓存
第二次请求
- 命中缓存,直接从缓存中取得数据
Redis
- MyBatis自动生成Id存储在Redis中,Key如下