在Mybatis中允许开发者自定义自己的缓存,本文将使用Redis作为Mybatis的二级缓存。在Mybatis中定义二级缓存,需要如下配置:
1、 MyBatis支持二级缓存的总开关:全局配置变量参数“cacheEnabled=true”
2、select语句所在的Mapper需配置了<cache> 或<cached-ref>节点
3、select语句的参数 useCache=true
Mybatis配置文件如下:
<settings> <!-- 这个配置使全局的映射器启用或禁用缓存 --> <setting name="cacheEnabled" value="true" /> <!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 --> <setting name="multipleResultSetsEnabled" value="true"/> <!-- 配置默认的执行器。SIMPLE 执行器没有什么特别之处。REUSE 执行器重用预处理语句。BATCH 执行器重用语句和批量更新 --> <setting name="defaultExecutorType" value="REUSE" /> <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 --> <setting name="lazyLoadingEnabled" value="false" /> <setting name="aggressiveLazyLoading" value="true" /> <!-- <setting name="enhancementEnabled" value="true"/> --> <!-- 设置超时时间,它决定驱动等待一个数据库响应的时间。 --> <setting name="defaultStatementTimeout" value="25000" /> </settings>
Mybatis的Mapper的配置文件如下:
<?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="user"> <!-- 二级缓存 --> <cache type="com.qunar.mobile.mybatis.cache.QRedisCache"/> <select id="getUsers" resultType="User" useCache="true"> select user_id as userId, user_name as username, user_desc as userDesc, create_time as createTime from bisystem_user </select> </mapper>
自定义二级缓存需要实现Mybatis的Cache接口,Redis缓存实现如下:
public class QRedisCache implements Cache { private String cacheId; private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); public QRedisCache(String cacheId) { if (cacheId == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.cacheId = cacheId; } @Override public String getId() { return cacheId; } @Override public void putObject(Object key, Object value) { JedisUtils.put(key, value); } @Override public Object getObject(Object key) { return JedisUtils.get(key); } @Override public Object removeObject(Object key) { return JedisUtils.remove(key); } @Override public void clear() { JedisUtils.removeAll(); } @Override public int getSize() { return 0; } @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } }
在QRedisCache使用的辅助类JedisUtils及SerializeUtils实现如下:
public class JedisUtils { private static final Logger logger = Logger.getLogger(JedisUtils.class); private static JedisPool JEDISPOOL; static { Properties props = new Properties(); try { props.load(JedisUtils.class.getResourceAsStream("/redis.properties")); JedisPoolConfig conf = new JedisPoolConfig(); conf.setMaxIdle(Integer.valueOf(props.getProperty("jedis.pool.maxIdle"))); conf.setTestOnBorrow(Boolean.valueOf(props.getProperty("jedis.pool.testOnBorrow"))); conf.setTestOnReturn(Boolean.valueOf(props.getProperty("jedis.pool.testOnReturn"))); JEDISPOOL = new JedisPool(conf, props.getProperty("redis.ip"), Integer.valueOf(props.getProperty("redis.port"))); } catch (IOException e) { logger.error("加载[jedis.properties]异常[" + e.getMessage() + "]", e); } } public static Jedis getJedis() { return JEDISPOOL.getResource(); } public static void recycleJedis(Jedis jedis) { jedis.close(); } /** * Redis存储Object序列化流 * */ public static void put(Object key, Object value) { Jedis jedis = getJedis(); jedis.set(SerializeUtils.serialize(key), SerializeUtils.serialize(value)); recycleJedis(jedis); } public static <T> T get(Object key) { Jedis jedis = getJedis(); T value = SerializeUtils.unserialize(jedis.get(SerializeUtils.serialize(key))); recycleJedis(jedis); return value; } public static Long remove(Object key) { Jedis jedis = getJedis(); Long num = jedis.del(SerializeUtils.serialize(key)); recycleJedis(jedis); return num; } public static void removeAll() { Jedis jedis = getJedis(); jedis.flushDB(); recycleJedis(jedis); } } public class SerializeUtils { private static final Logger logger = Logger.getLogger(SerializeUtils.class); private static void close(ObjectOutputStream objectOutputStream, ByteArrayOutputStream byteArrayOutputStream) { try { if (byteArrayOutputStream != null) { byteArrayOutputStream.close(); } if (objectOutputStream != null) { objectOutputStream.close(); } } catch (Exception e) { e.printStackTrace(); logger.error("关闭IO资源异常[" + e.getMessage() + "]", e); } } private static void close(ObjectInputStream objectInputStream, ByteArrayInputStream byteArrayInputStream) { try { if (objectInputStream != null) { objectInputStream.close(); } if (byteArrayInputStream != null) { byteArrayInputStream.close(); } } catch (Exception e) { e.printStackTrace(); logger.error("关闭IO资源异常[" + e.getMessage() + "]", e); } } public static byte[] serialize(Object object) { ObjectOutputStream objectOutputStream = null; ByteArrayOutputStream byteArrayOutputStream = null; try { byteArrayOutputStream = new ByteArrayOutputStream(); objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(object); byte[] bytes = byteArrayOutputStream.toByteArray(); return bytes; } catch (Exception e) { e.printStackTrace(); logger.error("序列化对象异常[" + e.getMessage() + "]", e); } finally { close(objectOutputStream, byteArrayOutputStream); } return null; } @SuppressWarnings("unchecked") public static <T> T unserialize(byte[] bytes) { if (bytes == null) return null; ByteArrayInputStream byteArrayInputStream = null; ObjectInputStream objectInputStream = null; try { byteArrayInputStream = new ByteArrayInputStream(bytes); objectInputStream = new ObjectInputStream(byteArrayInputStream); return (T) objectInputStream.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { close(objectInputStream, byteArrayInputStream); } return null; } }
Redis的配置文件redis.properties如下:
#redis服务器ip# redis.ip=192.168.2.107 #redis服务器端口号# redis.port=6379 #********jedis池参数设置********# #jedis的最大分配对象# jedis.pool.maxActive=3000 #jedis最大保存idel状态对象数 # jedis.pool.maxIdle=1000 #jedis池没有对象返回时,最大等待时间 # jedis.pool.maxWait=1500 #jedis调用borrowObject方法时,是否进行有效检查# jedis.pool.testOnBorrow=true #jedis调用returnObject方法时,是否进行有效检查 # jedis.pool.testOnReturn=true