mybatis通過配置檔案方式整合redis快取,替換mybatis二級快取
阿新 • • 發佈:2019-02-06
mybatis通過redis取代二級快取,二級快取的缺點不再贅述。
mybatis預設快取是PerpetualCache,可以檢視一下它的原始碼,發現其是Cache介面的實現;那麼我們的快取只要實現該介面即可。
該介面有以下方法需要實現:
String getId();
int getSize();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
ReadWriteLock getReadWriteLock();
下面開始實現程式碼
==============================華麗的分割線========================
1,RedisCache實現類
2,DummyReadWriteLock 類import org.apache.ibatis.cache.Cache; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; /** * Cache adapter for Redis. * * @author Eduardo Macarron */ public final class RedisCache implements Cache { private static JedisPool pool; private final ReadWriteLock readWriteLock = new DummyReadWriteLock(); private final String id; private final Integer expireSeconds; public RedisCache(final String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.id = id; final RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration(); pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(), redisConfig.getDatabase(), redisConfig.getClientName()); expireSeconds = redisConfig.getSettings().get(id) * 60; } @Override public void clear() { execute(new RedisCallback() { @Override public Object doWithRedis(Jedis jedis) { // jedis.del(id.toString()); throw new UnsupportedOperationException("not support redis-cache getsize method."); // return null; } }); } private Object execute(RedisCallback callback) { final Jedis jedis = pool.getResource(); try { return callback.doWithRedis(jedis); } finally { jedis.close(); } } @Override public String getId() { return this.id; } @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())); return SerializeUtil.unserialize(jedis.get(key.toString().getBytes())); } }); } @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } @Override public int getSize() { return (Integer) execute(new RedisCallback() { @Override public Object doWithRedis(Jedis jedis) { throw new UnsupportedOperationException("not support redis-cache getsize method."); } }); } @Override public void putObject(final Object key, final Object value) { execute(new RedisCallback() { @Override public Object doWithRedis(Jedis jedis) { System.out.println("快取----------------:" + key); jedis.set(key.toString().getBytes(), SerializeUtil.serialize(value)); if (expireSeconds > 0) { jedis.expire(key.toString().getBytes(), expireSeconds); } // jedis.hset(id.toString().getBytes(), key.toString().getBytes(), // SerializeUtil.serialize(value)); return null; } }); } @Override public Object removeObject(final Object key) { return execute(new RedisCallback() { @Override public Object doWithRedis(Jedis jedis) { // return jedis.hdel(id.toString(), key.toString()); // return jedis.del(key.toString()); throw new UnsupportedOperationException("not support redis-cache getsize method."); } }); } @Override public String toString() { return "Redis {" + id + "}"; } }
3,RedisCallback 介面import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; /** * @author Iwao AVE! */ class DummyReadWriteLock implements ReadWriteLock { static class DummyLock implements Lock { @Override public void lock() { // Not implemented } @Override public void lockInterruptibly() throws InterruptedException { // Not implemented } @Override public Condition newCondition() { return null; } @Override public boolean tryLock() { return true; } @Override public boolean tryLock(long paramLong, TimeUnit paramTimeUnit) throws InterruptedException { return true; } @Override public void unlock() { // Not implemented } } private final Lock lock = new DummyLock(); @Override public Lock readLock() { return lock; } @Override public Lock writeLock() { return lock; } }
import redis.clients.jedis.Jedis;
public interface RedisCallback {
Object doWithRedis(Jedis jedis);
}
4,RedisConfig 類
import java.util.Hashtable;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;
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;
private final Hashtable<String, Integer> settings = new Hashtable<String, Integer>();
public String getClientName() {
return clientName;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public int getDatabase() {
return database;
}
public String getHost() {
return host;
}
public String getPassword() {
return password;
}
public int getPort() {
return port;
}
public Hashtable<String, Integer> getSettings() {
return settings;
}
public int getSoTimeout() {
return soTimeout;
}
public void setClientName(String clientName) {
if ("".equals(clientName)) {
clientName = null;
}
this.clientName = clientName;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public void setDatabase(int database) {
this.database = database;
}
public void setHost(String host) {
if (host == null || "".equals(host)) {
host = Protocol.DEFAULT_HOST;
}
this.host = host;
}
public void setPassword(String password) {
if ("".equals(password)) {
password = null;
}
this.password = password;
}
public void setPort(int port) {
this.port = port;
}
public void setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
}
}
5,RedisConfigurationBuilder 類import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.cache.CacheException;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
/**
* Converter from the Config to a proper {@link RedisConfig}.
*
* @author Eduardo Macarron
*/
final class RedisConfigurationBuilder {
/**
* This class instance.
*/
private static final RedisConfigurationBuilder INSTANCE = new RedisConfigurationBuilder();
private static final String SYSTEM_PROPERTY_REDIS_PROPERTIES_FILENAME = "redis.properties.filename";
private static final String REDIS_RESOURCE = "redis.properties";
/**
* Return this class instance.
*
* @return this class instance.
*/
public static RedisConfigurationBuilder getInstance() {
return INSTANCE;
}
private final String redisPropertiesFilename;
/**
* Hidden constructor, this class can't be instantiated.
*/
private RedisConfigurationBuilder() {
redisPropertiesFilename = System.getProperty(SYSTEM_PROPERTY_REDIS_PROPERTIES_FILENAME, REDIS_RESOURCE);
}
private boolean isInteger(String s) {
return isInteger(s, 10);
}
private boolean isInteger(String s, int radix) {
if (s.isEmpty()) {
return false;
}
for (int i = 0; i < s.length(); i++) {
if (i == 0 && s.charAt(i) == '-') {
if (s.length() == 1) {
return false;
} else {
continue;
}
}
if (Character.digit(s.charAt(i), radix) < 0) {
return false;
}
}
return true;
}
/**
* Parses the Config and builds a new {@link RedisConfig}.
*
* @return the converted {@link RedisConfig}.
*/
public RedisConfig parseConfiguration() {
return parseConfiguration(getClass().getClassLoader());
}
/**
* Parses the Config and builds a new {@link RedisConfig}.
*
* @param the
* {@link ClassLoader} used to load the {@code memcached.properties}
* file in classpath.
* @return the converted {@link RedisConfig}.
*/
public RedisConfig parseConfiguration(ClassLoader classLoader) {
final Properties config = new Properties();
final InputStream input = classLoader.getResourceAsStream(redisPropertiesFilename);
if (input != null) {
try {
config.load(input);
} catch (final IOException e) {
throw new RuntimeException("An error occurred while reading classpath property '" + redisPropertiesFilename
+ "', see nested exceptions", e);
} finally {
try {
input.close();
} catch (final IOException e) {
// close quietly
}
}
}
final RedisConfig jedisConfig = new RedisConfig();
jedisConfig.setHost("localhost");
setConfigProperties(config, jedisConfig);
return jedisConfig;
}
private void setConfigProperties(Properties properties, RedisConfig jedisConfig) {
if (properties != null) {
final MetaObject metaCache = SystemMetaObject.forObject(jedisConfig);
for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
final String name = (String) entry.getKey();
final String value = (String) entry.getValue();
if (metaCache.hasSetter(name)) {
final Class<?> type = metaCache.getSetterType(name);
if (String.class == type) {
metaCache.setValue(name, value);
} else if (int.class == type || Integer.class == type) {
metaCache.setValue(name, Integer.valueOf(value));
} else if (long.class == type || Long.class == type) {
metaCache.setValue(name, Long.valueOf(value));
} else if (short.class == type || Short.class == type) {
metaCache.setValue(name, Short.valueOf(value));
} else if (byte.class == type || Byte.class == type) {
metaCache.setValue(name, Byte.valueOf(value));
} else if (float.class == type || Float.class == type) {
metaCache.setValue(name, Float.valueOf(value));
} else if (boolean.class == type || Boolean.class == type) {
metaCache.setValue(name, Boolean.valueOf(value));
} else if (double.class == type || Double.class == type) {
metaCache.setValue(name, Double.valueOf(value));
} else {
throw new CacheException("Unsupported property type: '" + name + "' of type " + type);
}
} else if (isInteger(value)) {
jedisConfig.getSettings().put(name, Integer.parseInt(value));
}
}
}
}
}
6,SerializeUtil 類import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.apache.ibatis.cache.CacheException;
public final class SerializeUtil {
public static byte[] serialize(Object object) {
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
final byte[] bytes = baos.toByteArray();
return bytes;
} catch (final Exception e) {
throw new CacheException(e);
}
}
public static Object unserialize(byte[] bytes) {
if (bytes == null) {
return null;
}
ByteArrayInputStream bais = null;
try {
bais = new ByteArrayInputStream(bytes);
final ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (final Exception e) {
throw new CacheException(e);
}
}
}
7,配置檔案(此處需要Goods實體,不再贅述),配置檔案中快取可以開關,如果某個select不需要快取,則加上 useCache="false" 屬性,預設不寫的話值是true<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.yjl.service.dao.GoodsDao">
<cache type="com.yjl.framework.caching.mybatis.RedisCache" />
<resultMap id="BaseResultMap" type="com.yjl.bean.Goods">
<id column="goods_id" property="goodsId" jdbcType="INTEGER" />
<result column="center_goods_id" property="centerGoodsId"
jdbcType="INTEGER" />
<result column="store_id" property="storeId" jdbcType="INTEGER" />
<result column="brand_id" property="brandId" jdbcType="INTEGER" />
</resultMap>
<select id="getGoodsById" resultMap="goodsDetail" parameterType="java.lang.Integer">
select goods_id,center_goods_id,store_id,brand_id from goods where goods_id = #{goodsId,jdbcType=INTEGER} limit 1
</select>
</mapper>
8,redis.properties(此處可以根據具體的namespace設定相應的快取時間)
host=localhost
port=6379
connectionTimeout=5000
soTimeout=4000
password=
database=0
com.yjl.service.dao.MealDao=5
com.yjl.service.dao.GoodsDao=10
程式碼部分已經完成,下面開始測試是否快取
===========================華麗的分割線===========================
下面開始測試
可以看出只有第一次會查詢資料庫,在redis快取時間之內不會在查詢資料庫
以上是本人實際工作中總結,如有錯誤請大家指正,歡迎大家積極評論。