概述

Mybatis默认缓存是PerpetualCache,它实现了Cache接口。Mybatis为了方便我们扩展缓存定义了一个Cache接口,因此,我们只需要参考源码自己使用redis实现Cache接口,即可达到Mybatis整合redis管理缓存的目的。

开始

本文介绍在spring-mvc的项目中,如何实现使用redis作为Mybatis的二级缓存。重点是实现Cache接口,而如何引入redis有多种方式,本文使用其中一种。

环境

  • jdk:1.7
  • redis:2.8.12
  • spring:4.1.1.RELEASE

新建redis.properties文件

用于记录redis的基本信息

 1# ====================================================
 2#                redis settings
 3# ====================================================
 4
 5redis.host=127.0.0.1
 6redis.port=6379
 7redis.pass=
 8redis.maxIdle=300
 9redis.maxActive=600
10redis.maxWait=1000
11redis.testOnBorrow=false

新建RedisConfig.java类

用于获取redis配置信息

 1public class RedisConfig {
 2
 3    private static String host;
 4    private static String port;
 5    private static String pass;
 6    private static String maxIdle;
 7    private static String maxActive;
 8    private static String maxWait;
 9    private static String testOnBorrow;   
10
11    public RedisConfig(String host, String port, String pass, String maxIdle,
12            String maxActive, String maxWait, String testOnBorrow) {
13        super();
14        RedisConfig.host = host;
15        RedisConfig.port = port;
16        RedisConfig.pass = pass;
17        RedisConfig.maxIdle = maxIdle;
18        RedisConfig.maxActive = maxActive;
19        RedisConfig.maxWait = maxWait;
20        RedisConfig.testOnBorrow = testOnBorrow;
21    }
22
23    public static String getHost() {
24        return host;
25    }
26
27    public static String getPort() {
28        return port;
29    }
30
31    public static String getPass() {
32        return pass;
33    }
34
35    public static String getMaxIdle() {
36        return maxIdle;
37    }
38
39    public static String getMaxActive() {
40        return maxActive;
41    }
42
43    public static String getMaxWait() {
44        return maxWait;
45    }
46
47    public static String getTestOnBorrow() {
48        return testOnBorrow;
49    }
50
51    public void setHost(String host) {
52        RedisConfig.host = host;
53    }
54
55    public void setPort(String port) {
56        RedisConfig.port = port;
57    }
58
59    public void setPass(String pass) {
60        RedisConfig.pass = pass;
61    }
62
63    public void setMaxIdle(String maxIdle) {
64        RedisConfig.maxIdle = maxIdle;
65    }
66
67    public void setMaxActive(String maxActive) {
68        RedisConfig.maxActive = maxActive;
69    }
70
71    public void setMaxWait(String maxWait) {
72        RedisConfig.maxWait = maxWait;
73    }
74
75    public void setTestOnBorrow(String testOnBorrow) {
76        RedisConfig.testOnBorrow = testOnBorrow;
77    }
78
79}

spring配置文件中构造RedisConfig

 1    <context:property-placeholder
 2        location="classpath:redis.properties"
 3        ignore-unresolvable="true" ignore-resource-not-found="true" />
 4
 5    <bean id="redisConfig" class="cn.essa.component.privilege.util.RedisConfig">
 6        <constructor-arg index="0">
 7            <value>${redis.host}</value>
 8        </constructor-arg>
 9        <constructor-arg index="1">
10            <value>${redis.port}</value>
11        </constructor-arg>
12        <constructor-arg index="2">
13            <value>${redis.pass}</value>
14        </constructor-arg>
15        <constructor-arg index="3">
16            <value>${redis.maxIdle}</value>
17        </constructor-arg>
18        <constructor-arg index="4">
19            <value>${redis.maxActive}</value>
20        </constructor-arg>
21        <constructor-arg index="5">
22            <value>${redis.maxWait}</value>
23        </constructor-arg>
24        <constructor-arg index="6">
25            <value>${redis.testOnBorrow}</value>
26        </constructor-arg>
27    </bean>

如此即可在普通类中使用RedisConfig,得到redis配置信息

新建RedisCache.java类

该类实现了org.apache.ibatis.cache.Cache接口

  1/**
  2 * @ClassName: RedisCache
  3 * @Description: 使用第三方缓存服务器redis,处理二级缓存
  4 * @author cipher
  5 *
  6 */
  7public class RedisCache implements Cache {
  8    private static final Log LOG = LogFactory.getLog(RedisCache.class);
  9    /** The ReadWriteLock. */
 10    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 11
 12    private String id;
 13
 14    public RedisCache(final String id) {
 15        if (id == null) {
 16            throw new IllegalArgumentException("必须传入ID");
 17        }
 18        LOG.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>MybatisRedisCache:id=" + id);
 19        this.id = id;
 20    }
 21
 22    @Override
 23    public String getId() {
 24        return this.id;
 25    }
 26
 27    @Override
 28    public int getSize() {
 29        Jedis jedis = null;
 30        JedisPool jedisPool = null;
 31        int result = 0;
 32        boolean borrowOrOprSuccess = true;
 33        try {
 34            jedis = CachePool.getInstance().getJedis();
 35            jedisPool = CachePool.getInstance().getJedisPool();
 36            result = Integer.valueOf(jedis.dbSize().toString());
 37        } catch (JedisConnectionException e) {
 38            LOG.error(e);
 39            borrowOrOprSuccess = false;
 40            if (jedis != null) {
 41                jedisPool.returnBrokenResource(jedis);
 42            }
 43        } finally {
 44            if (borrowOrOprSuccess) {
 45                jedisPool.returnResource(jedis);
 46            }
 47        }
 48        return result;
 49
 50    }
 51
 52    @Override
 53    public void putObject(Object key, Object value) {
 54        if (LOG.isDebugEnabled()) {
 55            LOG.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>putObject:"
 56                    + key.hashCode() + "=" + value);
 57        }
 58        if (LOG.isInfoEnabled()) {
 59            LOG.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>put to redis sql :"
 60                    + key.toString());
 61        }
 62        Jedis jedis = null;
 63        JedisPool jedisPool = null;
 64        boolean borrowOrOprSuccess = true;
 65        try {
 66            jedis = CachePool.getInstance().getJedis();
 67            jedisPool = CachePool.getInstance().getJedisPool();
 68            jedis.set(SerializeUtil.serialize(key.hashCode()),
 69                    SerializeUtil.serialize(value));
 70        } catch (JedisConnectionException e) {
 71            LOG.error(e);
 72            borrowOrOprSuccess = false;
 73            if (jedis != null) {
 74                jedisPool.returnBrokenResource(jedis);
 75            }
 76        } finally {
 77            if (borrowOrOprSuccess) {
 78                jedisPool.returnResource(jedis);
 79            }   
 80        }
 81
 82    }
 83
 84    @Override
 85    public Object getObject(Object key) {
 86        Jedis jedis = null;
 87        JedisPool jedisPool = null;
 88        Object value = null;
 89        boolean borrowOrOprSuccess = true;
 90        try {
 91            jedis = CachePool.getInstance().getJedis();
 92            jedisPool = CachePool.getInstance().getJedisPool();
 93            value = SerializeUtil.unserialize(jedis.get(SerializeUtil
 94                    .serialize(key.hashCode())));
 95        } catch (JedisConnectionException e) {
 96            LOG.error(e);
 97            borrowOrOprSuccess = false;
 98            if (jedis != null) {
 99                jedisPool.returnBrokenResource(jedis);
100            }  
101        } finally {
102            if (borrowOrOprSuccess) {
103                jedisPool.returnResource(jedis);
104            }  
105        }
106        if (LOG.isDebugEnabled()) {
107            LOG.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>getObject:"
108                    + key.hashCode() + "=" + value);
109        }
110        return value;
111    }
112
113    @Override
114    public Object removeObject(Object key) {
115        Jedis jedis = null;
116        JedisPool jedisPool = null;
117        Object value = null;
118        boolean borrowOrOprSuccess = true;
119        try {
120            jedis = CachePool.getInstance().getJedis();
121            jedisPool = CachePool.getInstance().getJedisPool();
122            value = jedis.expire(SerializeUtil.serialize(key.hashCode()), 0);
123        } catch (JedisConnectionException e) {
124            LOG.error(e);
125            borrowOrOprSuccess = false;
126            if (jedis != null) {
127                jedisPool.returnBrokenResource(jedis);
128            }
129        } finally {
130            if (borrowOrOprSuccess) {
131                jedisPool.returnResource(jedis);
132            }  
133        }
134        if (LOG.isDebugEnabled()) {
135            LOG.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>removeObject:"
136                    + key.hashCode() + "=" + value);
137        }
138        return value;
139    }
140
141    @Override
142    public void clear() {
143        Jedis jedis = null;
144        JedisPool jedisPool = null;
145        boolean borrowOrOprSuccess = true;
146        try {
147            jedis = CachePool.getInstance().getJedis();
148            jedisPool = CachePool.getInstance().getJedisPool();
149            jedis.flushDB();
150            jedis.flushAll();
151        } catch (JedisConnectionException e) {
152            LOG.error(e);
153            borrowOrOprSuccess = false;
154            if (jedis != null) {
155                jedisPool.returnBrokenResource(jedis);
156            }  
157        } finally {
158            if (borrowOrOprSuccess) {
159                jedisPool.returnResource(jedis);
160            }
161        }
162    }
163
164    @Override
165    public ReadWriteLock getReadWriteLock() {
166        return readWriteLock;
167    }
168
169    /**
170     *
171     * @ClassName: CachePool
172     * @Description: 单例Cache池
173     *
174     */
175    public static class CachePool {
176        JedisPool pool;
177        private static final CachePool CACHEPOOL = new CachePool();
178
179        public static CachePool getInstance() {
180            return CACHEPOOL;
181        }
182
183        private CachePool() {
184            try {
185                int maxIdle = Integer
186                        .valueOf(RedisConfig.getMaxIdle());
187                long maxWait = Long.valueOf(RedisConfig.getMaxWait());
188                int maxActive = Integer.valueOf(RedisConfig.getMaxActive());
189                String redisHost = RedisConfig.getHost();
190                int redisPort = Integer.valueOf(RedisConfig.getPort());
191                JedisPoolConfig config = new JedisPoolConfig();
192                config.setMaxIdle(maxIdle);
193                config.setMaxWait(maxWait);
194                config.setMaxActive(maxActive);
195                config.setTestOnBorrow(false);
196                pool = new JedisPool(config, redisHost, redisPort);
197            } catch (Exception e) {
198                LOG.error(e);
199                throw new RuntimeException("初始化连接池错误");
200            }
201        }
202
203        public Jedis getJedis() {
204            Jedis jedis = null;
205            boolean borrowOrOprSuccess = true;
206            try {
207                jedis = pool.getResource();
208            } catch (JedisConnectionException e) {
209                LOG.error(e);
210                borrowOrOprSuccess = false;
211                if (jedis != null) {
212                    pool.returnBrokenResource(jedis);
213                }
214            } finally {
215                if (borrowOrOprSuccess) {
216                    pool.returnResource(jedis);
217                }  
218            }
219            jedis = pool.getResource();
220            return jedis;
221        }
222
223        public JedisPool getJedisPool() {
224            return this.pool;
225        }
226
227    }
228
229    private static class SerializeUtil {
230        public static byte[] serialize(Object object) {
231            ObjectOutputStream oos = null;
232            ByteArrayOutputStream baos = null;
233            byte[] bytes = null;
234            try {
235                // 序列化
236                baos = new ByteArrayOutputStream();
237                oos = new ObjectOutputStream(baos);
238                oos.writeObject(object);
239                bytes = baos.toByteArray();
240                return bytes;
241            } catch (Exception e) {
242                LOG.error(e);
243            }
244            return bytes;
245        }
246
247        public static Object unserialize(byte[] bytes) {
248            if (bytes == null) {
249                return null;
250            }
251            ByteArrayInputStream bais = null;
252            try {
253                // 反序列化
254                bais = new ByteArrayInputStream(bytes);
255                ObjectInputStream ois = new ObjectInputStream(bais);
256                return ois.readObject();
257            } catch (Exception e) {
258                LOG.error(e);
259            }
260            return null;
261        }
262    }
263}

新建LoggingRedisCache.java类

该类继承了org.apache.ibatis.cache.decorators.LoggingCache,是缓存的入口类

1public class LoggingRedisCache extends LoggingCache {
2    public LoggingRedisCache(String id) {
3        super(new RedisCache(id));
4    }
5}

使用缓存

在需要使用缓存的mapper文件中加入(要在<mapper>标签范围内):

1<cache type="cn.essa.component.privilege.cache.LoggingRedisCache" />

注意:测试时请记得开启redis