redis作为现在最优秀的key-value数据库,非常适合提供项目的缓存服务。把redis作为mybatis的查询缓存也是很常见的做法。在网上发现N多人是自己做的Cache,其实在mybatis的git下有一个子项目mybatis-redis;这个项目提供了redis作为mybatis查询缓存的一个实现,下面先分析一下这个项目的实现原理,再提出几个项目的问题:

代码实现

该项目和大家普遍实现Mybatis的缓存方案大同小异,无非是实现Cache接口,并使用jedis操作缓存;不过该项目在设计细节上有一些区别;下面简要分析一下源码:

public final class RedisCache implements Cache {
public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(),
redisConfig.getDatabase(), redisConfig.getClientName());
}

RedisCache在mybatis启动的时候,由MyBatis的CacheBuilder创建,创建的方式很简单,就是调用RedisCache的带有String参数的构造方法,即RedisCache(String id);而在RedisCache的构造方法中,调用了RedisConfigurationBuilder来创建RedisConfig对象,并使用RedisConfig来创建JedisPool。

RedisConfig类继承了JedisPoolConfig,并提供了host,port等属性的包装,简单看一下RedisConfig的属性:

public class RedisConfig extends JedisPoolConfig {

private String host = Protocol.DEFAULT_HOST;
private int port = Protocol.DEFAULT_PORT;
private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;
private int soTimeout = Protocol.DEFAULT_TIMEOUT;
private String password;
private int database = Protocol.DEFAULT_DATABASE;
private String clientName;
}

RedisConfig对象是由RedisConfigurationBuilder创建的,简单看下这个类的主要方法:

public RedisConfig parseConfiguration(ClassLoader classLoader) {
Properties config = new Properties(); InputStream input = classLoader.getResourceAsStream(redisPropertiesFilename);
if (input != null) {
try {
config.load(input);
} catch (IOException e) {
throw new RuntimeException(
"An error occurred while reading classpath property '"
+ redisPropertiesFilename
+ "', see nested exceptions", e);
} finally {
try {
input.close();
} catch (IOException e) {
// close quietly
}
}
} RedisConfig jedisConfig = new RedisConfig();
setConfigProperties(config, jedisConfig);
return jedisConfig;
}

核心的方法就是parseConfiguration方法,该方法从classpath中读取一个redis.properties文件:

host=localhost
port=6379
connectionTimeout=5000
soTimeout=5000
password=
database=0
clientName=

并将该配置文件中的内容设置到RedisConfig对象中,并返回;

接下来,就是RedisCache使用RedisConfig类创建完成JedisPool;

在RedisCache中实现了一个简单的模板方法,用来操作Redis:

private Object execute(RedisCallback callback) {
Jedis jedis = pool.getResource();
try {
return callback.doWithRedis(jedis);
} finally {
jedis.close();
}
}

模板接口为RedisCallback,这个接口中就只需要实现了一个doWithRedis方法而已:

public interface RedisCallback {
Object doWithRedis(Jedis jedis);
}

接下来看看Cache中最重要的两个方法:putObject和getObject,通过这两个方法来查看mybatis-redis储存数据的格式:

@Override
public void putObject(final Object key, final Object value) {
execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
jedis.hset(id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));
return null;
}
});
} @Override
public Object getObject(final Object key) {
return execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
return SerializeUtil.unserialize(jedis.hget(id.toString().getBytes(), key.toString().getBytes()));
}
});
}

可以很清楚的看到,mybatis-redis在存储数据的时候,是使用的hash结构,把cache的id作为这个hash的key(cache的id在mybatis中就是mapper的namespace);这个mapper中的查询缓存数据作为hash的field,需要缓存的内容直接使用SerializeUtil存储,SerializeUtil和其他的序列化类差不多,负责对象的序列化和反序列化;

使用方式

整个mybatis-redis的关键代码就这些,通过对代码的分析,我们很容易得到mybatis-redis的使用方式:

1,在项目中添加一个redis.properties配置;

2,直接在mapper中使用<cache type="org.mybatis.caches.redis.RedisCache" />即可;

分析

通过代码,我们可以看到在实际应用当中可能存在的一些问题:

  1. 默认情况下,mybatis会为每一个mapper创建一个RedisCache,而JedisPool是在RedisCache的构造方法中创建的,这就意味着会为每一个mapper创建一个JedisPool,使用意图和开销上面都会有问题;
  2. 在很多情况下,我们的应用中也会独立使用到redis,这样也无法让RedisCache直接使用我们项目中可能已经存在的JedisPool;并且会造成两个配置文件(除非我们应用也使用redis.properties);
  3. RedisCache是使用hash来缓存一个Mapper中的查询,所以我们只能通过mybatis的cache配置来控制对象的生存时间,空闲时间等属性;而无法独立的去配置每一个缓存区域(即每一个hash);
文/小码哥Java学院(简书作者)

原文链接:http://www.jianshu.com/p/648c10420df1

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

mybatis-redis项目分析的更多相关文章

  1. SpringMVC + Spring + Mybatis+ Redis +shiro以及MyBatis学习

    SpringMVC + Spring + Mybatis+ Redis +shiro http://www.sojson.com/shiro MyBatis简介与配置MyBatis+Spring+My ...

  2. Spring Boot + Mybatis + Redis二级缓存开发指南

    Spring Boot + Mybatis + Redis二级缓存开发指南 背景 Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一.Mybat ...

  3. springboot整合mybatis,redis,代码(二)

    一 说明: springboot整合mybatis,redis,代码(一) 这个开发代码的复制粘贴,可以让一些初学者直接拿过去使用,且没有什么bug 二 对上篇的说明 可以查看上图中文件: 整个工程包 ...

  4. 如何玩转最新的项目的搭配springmvc+mybatis+Redis+Nginx+tomcat+mysql

    上一次完成nginx+tomcat组合搭配,今天我们就说说,这几个软件在项目中充当的角色: 要想完成这几个软件的组合,我们必须知道和熟悉应用这个框架, 一: Nginx:在项目中大多数作为反向代理服务 ...

  5. SpringCloud+MyBatis+Redis整合—— 超详细实例(二)

    2.SpringCloud+MyBatis+Redis redis①是一种nosql数据库,以键值对<key,value>的形式存储数据,其速度相比于MySQL之类的数据库,相当于内存读写 ...

  6. springmvc+spring-security+mybatis +redis +solar框架抽取

    参考文章:Spring MVC 3 深入总结: 第二章 Spring MVC入门 —— 跟开涛学SpringMVC 参考博客:http://www.cnblogs.com/liukemng/categ ...

  7. springmvc+mybatis+redis(转)

    最近在学习redis的使用方法,它的本地使用方法比较简单,只需要先启动Redis服务器,然后运行测试代码即可.但是现在我想要在网站上访问数据库的时候采用Redis缓存,问题就出来了.要么是缓存直接失效 ...

  8. Springboot Mybatis Redis 实现二级缓存

    前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...

  9. springboot mybatis redis 二级缓存

    前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...

  10. springboot+mybatis+redis实现分布式缓存

    大家都知道springboot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群都能同步公共模块的缓存数据, ...

随机推荐

  1. 全新的ASP.NET !

    全新的ASP.NET ! 背景 最新版本的 ASP.NET 叫做 ASP.NET Core (也被称为 ASP.NET 5)   它颠覆了过去的 ASP.NET. 什么是 ASP.NET Core? ...

  2. 射频识别技术漫谈(14)——Mifare S50与S70的存取控制

    存取控制指符合什么条件才能对卡片进行操作. S50和S70的块分为数据块和控制块,对数据块的操作有“读”.“写”.“加值”.“减值(含传输和存储)”四种,对控制块的操作只有“读”和“写”两种. S50 ...

  3. protel99se中做拼板图解

    很多时候我们要在protel99se中做拼板, 但是通常在复制进行拼版的时候会出现如下的效果,元件被重新命名了. 而无法达到我们需要的像下图的效果 那我们怎么办,才能达到上图的效果呢?其实操作很简单. ...

  4. C/C++程序员面试大纲

    基础篇:操作系统.计算机网络.设计模式一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会写 ...

  5. 给定一个字符串里面只有"R" "G" "B" 三个字符,请排序,最终结果的顺序是R在前 G中 B在后。 要求:空间复杂度是O(1),且只能遍历一次字符串。

    题目:给定一个字符串里面只有"R" "G" "B" 三个字符,请排序,最终结果的顺序是R在前 G中 B在后. 要求:空间复杂度是O(1),且 ...

  6. Eclipse 代码提示不显示的问题

    在备份Android SDK和Eclipse的时候出了问题,然后只能重新下载,SDK下得我都要吐血了,超慢.然后,我发现Eclipse有了新版本的,于是就下载了个新版的,结果出了问题了.所有的Andr ...

  7. CSS中的repeat

    Repeat-x是横向铺满,就是图片会横向重复,直到铺满. Repeat-y是纵向铺满,就是让图片纵向重复,直到铺满. 如果不想让重复,就直接为:no-repeat.

  8. 如何隐藏DLL中,导出函数的名称?

    一.引言 很多时候,我们写了一个Dll,不希望别人通过DLL查看工具,看到我们的导出函数名称.可以通过以下步骤实现: 1. 在def函数中做如下定义: LIBRARY EXPORTS HideFunc ...

  9. 新建linux组、用户命令

    之前在安装oracle的时候,出现了一个问题:安装Oracle,新建组.用户的时候的一个错误.看这篇博客前,先看这个链接,学习要从解决出现的问题出手. 建立用户组和用户 下面总结一下Linux建立组和 ...

  10. TC基础使用指南(基于xbeta的TC配置文件)

    所有常用目录都可以通过ctrl+d加一个或几个字母的超快捷方式直接跳转到位. 按下BackSpace键,就可以进入到上一级目录 Ctrl+q 在右侧打开左侧选定文件,再按一次Ctrl+q退出 按 Ct ...