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

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

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

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

下面开始测试

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


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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值