redis作為現(xiàn)在最優(yōu)秀的key-value數(shù)據(jù)庫盏浙,非常適合提供項(xiàng)目的緩存服務(wù)系羞。把redis作為mybatis的查詢緩存也是很常見的做法复罐。在網(wǎng)上發(fā)現(xiàn)N多人是自己做的Cache匣掸,其實(shí)在mybatis的git下有一個(gè)子項(xiàng)目mybatis-redis趟紊;這個(gè)項(xiàng)目提供了redis作為mybatis查詢緩存的一個(gè)實(shí)現(xiàn),下面先分析一下這個(gè)項(xiàng)目的實(shí)現(xiàn)原理碰酝,再提出幾個(gè)項(xiàng)目的問題:
代碼實(shí)現(xiàn)
該項(xiàng)目和大家普遍實(shí)現(xiàn)Mybatis的緩存方案大同小異霎匈,無非是實(shí)現(xiàn)Cache接口,并使用jedis操作緩存送爸;不過該項(xiàng)目在設(shè)計(jì)細(xì)節(jié)上有一些區(qū)別铛嘱;下面簡要分析一下源碼:
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啟動的時(shí)候暖释,由MyBatis的CacheBuilder創(chuàng)建,創(chuàng)建的方式很簡單墨吓,就是調(diào)用RedisCache的帶有String參數(shù)的構(gòu)造方法球匕,即RedisCache(String id);而在RedisCache的構(gòu)造方法中帖烘,調(diào)用了RedisConfigurationBuilder來創(chuàng)建RedisConfig對象亮曹,并使用RedisConfig來創(chuàng)建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創(chuàng)建的,簡單看下這個(gè)類的主要方法:
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中讀取一個(gè)redis.properties文件:
host=localhost
port=6379
connectionTimeout=5000
soTimeout=5000
password=
database=0
clientName=
并將該配置文件中的內(nèi)容設(shè)置到RedisConfig對象中役耕,并返回;
接下來聪廉,就是RedisCache使用RedisConfig類創(chuàng)建完成JedisPool瞬痘;
在RedisCache中實(shí)現(xiàn)了一個(gè)簡單的模板方法,用來操作Redis:
private Object execute(RedisCallback callback) {
Jedis jedis = pool.getResource();
try {
return callback.doWithRedis(jedis);
} finally {
jedis.close();
}
}
模板接口為RedisCallback锄列,這個(gè)接口中就只需要實(shí)現(xiàn)了一個(gè)doWithRedis方法而已:
public interface RedisCallback {
Object doWithRedis(Jedis jedis);
}
接下來看看Cache中最重要的兩個(gè)方法:putObject和getObject图云,通過這兩個(gè)方法來查看mybatis-redis儲存數(shù)據(jù)的格式:
@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在存儲數(shù)據(jù)的時(shí)候邻邮,是使用的hash結(jié)構(gòu),把cache的id作為這個(gè)hash的key(cache的id在mybatis中就是mapper的namespace)克婶;這個(gè)mapper中的查詢緩存數(shù)據(jù)作為hash的field筒严,需要緩存的內(nèi)容直接使用SerializeUtil存儲,SerializeUtil和其他的序列化類差不多情萤,負(fù)責(zé)對象的序列化和反序列化鸭蛙;
使用方式
整個(gè)mybatis-redis的關(guān)鍵代碼就這些,通過對代碼的分析筋岛,我們很容易得到mybatis-redis的使用方式:
1娶视,在項(xiàng)目中添加一個(gè)redis.properties配置;
2睁宰,直接在mapper中使用<cache type="org.mybatis.caches.redis.RedisCache" />即可肪获;
分析
通過代碼,我們可以看到在實(shí)際應(yīng)用當(dāng)中可能存在的一些問題:
1.默認(rèn)情況下柒傻,mybatis會為每一個(gè)mapper創(chuàng)建一個(gè)RedisCache孝赫,而JedisPool是在RedisCache的構(gòu)造方法中創(chuàng)建的,這就意味著會為每一個(gè)mapper創(chuàng)建一個(gè)JedisPool红符,使用意圖和開銷上面都會有問題青柄;
2.在很多情況下伐债,我們的應(yīng)用中也會獨(dú)立使用到redis,這樣也無法讓RedisCache直接使用我們項(xiàng)目中可能已經(jīng)存在的JedisPool致开;并且會造成兩個(gè)配置文件(除非我們應(yīng)用也使用redis.properties)峰锁;
3.RedisCache是使用hash來緩存一個(gè)Mapper中的查詢,所以我們只能通過mybatis的cache配置來控制對象的生存時(shí)間双戳,空閑時(shí)間等屬性祖今;而無法獨(dú)立的去配置每一個(gè)緩存區(qū)域(即每一個(gè)hash);
來源:
http://bbs.520it.com/forum.php?mod=viewthread&tid=286&extra=page%3D4