mybatis通过配置文件方式整合redis缓存,替换mybatis二级缓存

来源:互联网 发布:网络棋牌游戏赌博平台 编辑:程序博客网 时间:2024/05/18 22:09

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实现类

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 + "}";  }}
2,DummyReadWriteLock 

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;  }}
3,RedisCallback 接口

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=localhostport=6379connectionTimeout=5000soTimeout=4000password=database=0com.yjl.service.dao.MealDao=5com.yjl.service.dao.GoodsDao=10

代码部分已经完成,下面开始测试是否缓存

===========================华丽的分割线===========================

下面开始测试

可以看出只有第一次会查询数据库,在redis缓存时间之内不会在查询数据库


以上是本人实际工作中总结,如有错误请大家指正,欢迎大家积极评论。

1 0
原创粉丝点击