代码改变世界

MyBatis系列目录--5. MyBatis一级缓存和二级缓存(redis实现)

2017-07-10 11:15  dogdogwang  阅读(455)  评论(0编辑  收藏  举报

转载请注明出处哈:http://carlosfu.iteye.com/blog/2238662


0. 相关知识:
查询缓存:绝大数系统主要是读多写少。
缓存作用:减轻数据库压力,提供访问速度。

  

1. 一级缓存测试用例

(1) 默认开启,不需要有什么配置

(2) 示意图


 

(3) 测试代码

Java代码  收藏代码
  1. package com.sohu.tv.cache;  
  2. import org.apache.ibatis.session.SqlSession;  
  3. import org.junit.After;  
  4. import org.junit.Before;  
  5. import org.junit.Test;  
  6. import com.sohu.tv.bean.Player;  
  7. import com.sohu.tv.mapper.PlayerDao;  
  8. import com.sohu.tv.test.mapper.BaseTest;  
  9. /** 
  10.  * 一级缓存测试 
  11.  *  
  12.  * @author leifu 
  13.  * @Date 2015-8-3 
  14.  * @Time 下午9:51:00 
  15.  */  
  16. public class FirstCacheTest extends BaseTest {  
  17.     private SqlSession sqlSession;  
  18.     private SqlSession sqlSessionAnother;  
  19.   
  20.       
  21.     @Before  
  22.     public void before() {  
  23.         sqlSession = sessionFactory.openSession(false);  
  24.         sqlSessionAnother = sessionFactory.openSession(false);  
  25.     }  
  26.     @After  
  27.     public void after() {  
  28.         sqlSession.close();  
  29.         sqlSessionAnother.close();  
  30.     }  
  31.     @Test  
  32.     public void test1() throws Exception {  
  33.         PlayerDao playerDao = sqlSession.getMapper(PlayerDao.class);  
  34.         Player player = playerDao.getPlayerById(1);  
  35.         System.out.println(player);  
  36.           
  37.         playerDao = sqlSession.getMapper(PlayerDao.class);  
  38.         player = playerDao.getPlayerById(1);  
  39.         System.out.println(player);  
  40.           
  41.         playerDao = sqlSessionAnother.getMapper(PlayerDao.class);  
  42.         player = playerDao.getPlayerById(1);  
  43.         System.out.println(player);  
  44.           
  45.     }  
  46.       
  47.     @Test  
  48.     public void test2() throws Exception {  
  49.         PlayerDao playerDao = sqlSession.getMapper(PlayerDao.class);  
  50.         Player player = playerDao.getPlayerById(1);  
  51.         System.out.println(player);  
  52.           
  53.         //1. session清除或者提交  
  54. //        sqlSession1.commit();  
  55. //        sqlSession.clearCache();  
  56.           
  57.         //2. 增删改查  
  58. //        playerDao.savePlayer(new Player(-1, "abcd", 13));  
  59. //        playerDao.updatePlayer(new Player(4, "abcd", 13));  
  60.         playerDao.deletePlayer(4);  
  61.           
  62.         player = playerDao.getPlayerById(1);  
  63.         System.out.println(player);  
  64.           
  65.     }  
  66.       
  67.       
  68. }  

 

2、二级缓存(自带 PerpetualCache)

(0) 示意图



 

(1) 二级缓存需要开启

总配置文件中,二级缓存也是开启的,不需要设置

Xml代码  收藏代码
  1. <setting name="cacheEnabled" value="true"/>  

mapper级别的cache需要开启,在对应的mapper.xml写入

Xml代码  收藏代码
  1. <!--开启本mapper的二级缓存-->  
  2. <cache/>  

(2) 实体类在二级缓存中需要进行序列化,所以所有实体类需要实现Serializable 

(3) 示例:

Java代码  收藏代码
  1. package com.sohu.tv.cache;  
  2. import org.apache.ibatis.session.SqlSession;  
  3. import org.junit.After;  
  4. import org.junit.Before;  
  5. import org.junit.Test;  
  6. import com.sohu.tv.bean.Player;  
  7. import com.sohu.tv.mapper.PlayerDao;  
  8. import com.sohu.tv.test.mapper.BaseTest;  
  9. /** 
  10.  * 二级缓存测试 
  11.  *  
  12.  * @author leifu 
  13.  * @Date 2015-8-3 
  14.  * @Time 下午10:10:34 
  15.  */  
  16. public class SecondCacheTest extends BaseTest {  
  17.     private SqlSession sqlSession1 = sessionFactory.openSession();  
  18.       
  19.     private SqlSession sqlSession2 = sessionFactory.openSession();  
  20.       
  21.     private SqlSession sqlSession3 = sessionFactory.openSession();  
  22.       
  23.     private PlayerDao playerDao1;  
  24.       
  25.     private PlayerDao playerDao2;  
  26.       
  27.     private PlayerDao playerDao3;  
  28.       
  29.     @Before  
  30.     public void before() {  
  31.         sqlSession1 = sessionFactory.openSession(false);  
  32.         sqlSession2 = sessionFactory.openSession(false);  
  33.         sqlSession3 = sessionFactory.openSession(false);  
  34.           
  35.         playerDao1 = sqlSession1.getMapper(PlayerDao.class);  
  36.         playerDao2 = sqlSession2.getMapper(PlayerDao.class);  
  37.         playerDao3 = sqlSession3.getMapper(PlayerDao.class);  
  38.     }  
  39.     @After  
  40.     public void after() {  
  41.         sqlSession1.close();  
  42.         sqlSession2.close();  
  43.         sqlSession3.close();  
  44.     }  
  45.        
  46.     @Test  
  47.     public void test1() throws Exception {  
  48.         int targetId = 1;  
  49.           
  50.         //session1 查询并提交  
  51.         Player player1 = playerDao1.getPlayerById(targetId);  
  52.         System.out.println("player1: " + player1);  
  53.         sqlSession1.commit();  
  54.           
  55.         //session2 命中后,更新并提交清空缓存  
  56.         Player player2 = playerDao2.getPlayerById(targetId);  
  57.         System.out.println("player2: " + player2);  
  58.         player2.setAge(15);  
  59.         playerDao2.update(player2);  
  60.         sqlSession2.commit();  
  61.           
  62.         //session3 不命中  
  63.         Player player3 = playerDao3.getPlayerById(targetId);  
  64.         System.out.println("player3: " + player3);  
  65.     }  
  66.       
  67.     @Test  
  68.     public void test2() throws Exception {  
  69.         int one = 1;  
  70.         int two = 2;  
  71.           
  72.         //session1 查询并提交  
  73.         Player player1 = playerDao1.getPlayerById(one);  
  74.         playerDao1.getPlayerById(two);  
  75.         System.out.println("player1: " + player1);  
  76.         sqlSession1.commit();  
  77.           
  78.         //session2 命中后,更新并提交清空缓存  
  79.         Player player2 = playerDao2.getPlayerById(one);  
  80.         System.out.println("player2: " + player2);  
  81.         player2.setAge(15);  
  82.         playerDao2.updatePlayer(player2);  
  83.         sqlSession2.commit();  
  84.           
  85.         //session3 不命中  
  86.         Player player3 = playerDao3.getPlayerById(two);  
  87.         System.out.println("player3: " + player3);  
  88.     }  
  89.       
  90.       
  91. }  

(4) 重要日志:

Java代码  收藏代码
  1. 22:24:37.191 [main] DEBUG com.sohu.tv.mapper.PlayerDao - Cache Hit Ratio [com.sohu.tv.mapper.PlayerDao]: 0.0  
  2. 22:24:37.196 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection  
  3. 22:24:37.460 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 1695520324.  
  4. 22:24:37.460 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  5. 22:24:37.463 [main] DEBUG c.s.t.mapper.PlayerDao.getPlayerById - ==> Preparing: select id,name,age from players where id=?   
  6. 22:24:37.520 [main] DEBUG c.s.t.mapper.PlayerDao.getPlayerById - ==> Parameters: 1(Integer)  
  7. 22:24:37.541 [main] DEBUG c.s.t.mapper.PlayerDao.getPlayerById - <== Total: 1  
  8. player1: Player [id=1, name=kaka, age=60]  
  9. 22:24:37.549 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  10. 22:24:37.549 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  11. 22:24:37.549 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Returned connection 1695520324 to pool.  
  12. 22:29:13.203 [main] DEBUG com.sohu.tv.mapper.PlayerDao - Cache Hit Ratio [com.sohu.tv.mapper.PlayerDao]: 0.5  
  13. player3: Player [id=1, name=kaka, age=60]  
  14. 22:29:13.204 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection  
  15. 22:29:13.204 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Checked out connection 1695520324 from pool.  
  16. 22:29:13.204 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  17. 22:29:13.205 [main] DEBUG c.s.tv.mapper.PlayerDao.updatePlayer - ==> Preparing: update players set name=?,age=? where id=?   
  18. 22:29:13.207 [main] DEBUG c.s.tv.mapper.PlayerDao.updatePlayer - ==> Parameters: kaka(String), 60(Integer), 1(Integer)  
  19. 22:29:13.208 [main] DEBUG c.s.tv.mapper.PlayerDao.updatePlayer - <== Updates: 1  
  20. 22:29:13.210 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  21. 22:29:13.210 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  22. 22:29:13.211 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  23. 22:29:13.211 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Returned connection 1695520324 to pool.  
  24. 22:29:13.211 [main] DEBUG com.sohu.tv.mapper.PlayerDao - Cache Hit Ratio [com.sohu.tv.mapper.PlayerDao]: 0.3333333333333333  
  25. 22:29:13.211 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection  
  26. 22:29:13.212 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Checked out connection 1695520324 from pool.  
  27. 22:29:13.212 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  28. 22:29:13.212 [main] DEBUG c.s.t.mapper.PlayerDao.getPlayerById - ==> Preparing: select id,name,age from players where id=?   
  29. 22:29:13.213 [main] DEBUG c.s.t.mapper.PlayerDao.getPlayerById - ==> Parameters: 1(Integer)  
  30. 22:29:13.214 [main] DEBUG c.s.t.mapper.PlayerDao.getPlayerById - <== Total: 1  
  31. player2: Player [id=1, name=kaka, age=60]  
  32. 22:29:13.215 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  33. 22:29:13.216 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@650f9644]  
  34. 22:29:13.216 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Returned connection 1695520324 to pool.  
 

 

 

3、二级缓存(Redis版)

(1) redis使用一个简单的单点实例作为数据源:

引入jedis pom依赖:

Xml代码  收藏代码
  1. <jedis.version>2.8.0</jedis.version>  
  2. <protostuff.version>1.0.8</protostuff.version>  
  3. <dependency>  
  4.     <groupId>redis.clients</groupId>  
  5.     <artifactId>jedis</artifactId>  
  6.     <version>${jedis.version}</version>  
  7. </dependency>  
  8. <dependency>  
  9.     <groupId>com.dyuproject.protostuff</groupId>  
  10.     <artifactId>protostuff-runtime</artifactId>  
  11.     <version>${protostuff.version}</version>  
  12. </dependency>  
  13.   
  14. <dependency>  
  15.     <groupId>com.dyuproject.protostuff</groupId>  
  16.     <artifactId>protostuff-core</artifactId>  
  17.     <version>${protostuff.version}</version>  
  18. </dependency>  

 

jedis获取工具(使用jedispool)

Java代码  收藏代码
  1. package com.sohu.tv.redis;  
  2. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;  
  3. import org.slf4j.Logger;  
  4. import org.slf4j.LoggerFactory;  
  5. import redis.clients.jedis.JedisPool;  
  6. /** 
  7.  * jedisPool获取工具 
  8.  *  
  9.  * @author leifu 
  10.  * @Date 2015年8月4日 
  11.  * @Time 上午9:01:45 
  12.  */  
  13. public class RedisStandAloneUtil {  
  14.     private final static Logger logger = LoggerFactory.getLogger(RedisStandAloneUtil.class);  
  15.     /** 
  16.      * jedis连接池 
  17.      */  
  18.     private static JedisPool jedisPool;  
  19.        
  20.     /** 
  21.      * redis-host 
  22.      */  
  23.     private final static String REDIS_HOST = "10.10.xx.xx";  
  24.        
  25.     /** 
  26.      * redis-port 
  27.      */  
  28.     private final static int REDIS_PORT = 6384;  
  29.        
  30.     static {  
  31.         try {  
  32.             jedisPool = new JedisPool(new GenericObjectPoolConfig(), REDIS_HOST, REDIS_PORT);  
  33.         } catch (Exception e) {  
  34.             logger.error(e.getMessage(), e);  
  35.         }  
  36.     }  
  37.     public static JedisPool getJedisPool() {  
  38.         return jedisPool;  
  39.     }  
  40.         
  41.     public static void main(String[] args) {  
  42.         System.out.println(RedisStandAloneUtil.getJedisPool().getResource().info());  
  43.     }  
  44. }  

 

(2) 如果自己实现mybatis的二级缓存,需要实现org.apache.ibatis.cache.Cache接口,已经实现的有如下:


 

 

序列化相关工具代码:

Java代码  收藏代码
  1. package com.sohu.tv.redis.serializable;  
  2.   
  3.   
  4.   
  5. import com.dyuproject.protostuff.LinkedBuffer;  
  6. import com.dyuproject.protostuff.ProtostuffIOUtil;  
  7. import com.dyuproject.protostuff.Schema;  
  8. import com.dyuproject.protostuff.runtime.RuntimeSchema;  
  9.   
  10. import java.util.concurrent.ConcurrentHashMap;  
  11.   
  12. public class ProtostuffSerializer {  
  13.   
  14.     private static ConcurrentHashMap<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();  
  15.   
  16.     public <T> byte[] serialize(final T source) {  
  17.         VO<T> vo = new VO<T>(source);  
  18.   
  19.         final LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);  
  20.         try {  
  21.             final Schema<VO> schema = getSchema(VO.class);  
  22.             return serializeInternal(vo, schema, buffer);  
  23.         } catch (final Exception e) {  
  24.             throw new IllegalStateException(e.getMessage(), e);  
  25.         } finally {  
  26.             buffer.clear();  
  27.         }  
  28.     }  
  29.   
  30.     public <T> T deserialize(final byte[] bytes) {  
  31.         try {  
  32.             Schema<VO> schema = getSchema(VO.class);  
  33.             VO vo = deserializeInternal(bytes, schema.newMessage(), schema);  
  34.             if (vo != null && vo.getValue() != null) {  
  35.                 return (T) vo.getValue();  
  36.             }  
  37.         } catch (final Exception e) {  
  38.             throw new IllegalStateException(e.getMessage(), e);  
  39.         }  
  40.         return null;  
  41.     }  
  42.   
  43.     private <T> byte[] serializeInternal(final T source, final Schema<T> schema, final LinkedBuffer buffer) {  
  44.         return ProtostuffIOUtil.toByteArray(source, schema, buffer);  
  45.     }  
  46.   
  47.     private <T> T deserializeInternal(final byte[] bytes, final T result, final Schema<T> schema) {  
  48.         ProtostuffIOUtil.mergeFrom(bytes, result, schema);  
  49.         return result;  
  50.     }  
  51.   
  52.     private static <T> Schema<T> getSchema(Class<T> clazz) {  
  53.         @SuppressWarnings("unchecked")  
  54.         Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);  
  55.         if (schema == null) {  
  56.             schema = RuntimeSchema.createFrom(clazz);  
  57.             cachedSchema.put(clazz, schema);  
  58.         }  
  59.         return schema;  
  60.     }  
  61.   
  62. }  

 

Java代码  收藏代码
  1. package com.sohu.tv.redis.serializable;  
  2.   
  3.   
  4. import java.io.Serializable;  
  5.   
  6. public class VO<T> implements Serializable {  
  7.   
  8.     private T value;  
  9.   
  10.     public VO(T value) {  
  11.         this.value = value;  
  12.     }  
  13.   
  14.     public VO() {  
  15.     }  
  16.   
  17.     public T getValue() {  
  18.         return value;  
  19.     }  
  20.   
  21.     @Override  
  22.     public String toString() {  
  23.         return "VO{" +  
  24.                 "value=" + value +  
  25.                 '}';  
  26.     }  
  27. }  

 
 

Redis需要自己来实现,代码如下:

Java代码  收藏代码
  1. package com.sohu.tv.redis;  
  2. import java.util.concurrent.locks.ReadWriteLock;  
  3. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  4. import org.apache.ibatis.cache.Cache;  
  5. import org.slf4j.Logger;  
  6. import org.slf4j.LoggerFactory;  
  7. import redis.clients.jedis.Jedis;  
  8. import redis.clients.jedis.serializable.ProtostuffSerializer;  
  9. /** 
  10.  * mybatis redis实现 
  11.  *  
  12.  * @author leifu 
  13.  * @Date 2015年8月4日 
  14.  * @Time 上午9:12:37 
  15.  */  
  16. public class MybatisRedisCache implements Cache {  
  17.     private static Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);  
  18.     private String id;  
  19.     private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();  
  20.     private final ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();  
  21.     public MybatisRedisCache(final String id) {  
  22.         if (logger.isInfoEnabled()) {  
  23.             logger.info("============ MybatisRedisCache id {} ============", id);  
  24.         }  
  25.         if (id == null) {    
  26.             throw new IllegalArgumentException("Cache instances require an ID");    
  27.         }    
  28.         this.id = id;    
  29.     }   
  30.        
  31.     @Override  
  32.     public String getId() {  
  33.         return this.id;  
  34.     }  
  35.     @Override  
  36.     public int getSize() {  
  37.         Jedis jedis = null;  
  38.         int size = -1;  
  39.         try {  
  40.             jedis = RedisStandAloneUtil.getJedisPool().getResource();  
  41.             size = Integer.valueOf(jedis.dbSize().toString());  
  42.         } catch (Exception e) {  
  43.             logger.error(e.getMessage(), e);  
  44.         } finally {  
  45.             if (jedis != null) {  
  46.                 jedis.close();  
  47.             }  
  48.         }  
  49.         return size;  
  50.     }  
  51.     @Override  
  52.     public void putObject(Object key, Object value) {  
  53.         if (logger.isInfoEnabled()) {  
  54.             logger.info("============ putObject key: {}, value: {} ============", key, value);  
  55.         }  
  56.         Jedis jedis = null;  
  57.         try {  
  58.             jedis = RedisStandAloneUtil.getJedisPool().getResource();  
  59.             byte[] byteKey = protostuffSerializer.serialize(key);  
  60.             byte[] byteValue = protostuffSerializer.serialize(value);  
  61.             jedis.set(byteKey, byteValue);  
  62.         } catch (Exception e) {  
  63.             logger.error(e.getMessage(), e);  
  64.         } finally {  
  65.             if (jedis != null) {  
  66.                 jedis.close();  
  67.             }  
  68.         }  
  69.     }  
  70.     @Override  
  71.     public Object getObject(Object key) {  
  72.         if (logger.isInfoEnabled()) {  
  73.             logger.info("============ getObject key: {}============", key);  
  74.         }  
  75.         Object object = null;  
  76.         Jedis jedis = null;  
  77.         try {  
  78.             jedis = RedisStandAloneUtil.getJedisPool().getResource();  
  79.             byte[] bytes = jedis.get(protostuffSerializer.serialize(key));  
  80.             if (bytes != null) {  
  81.                 object = protostuffSerializer.deserialize(bytes);  
  82.             }  
  83.         } catch (Exception e) {  
  84.             logger.error(e.getMessage(), e);  
  85.         } finally {  
  86.             if (jedis != null) {  
  87.                 jedis.close();  
  88.             }  
  89.         }  
  90.         return object;  
  91.     }  
  92.     @Override  
  93.     public Object removeObject(Object key) {  
  94.         if (logger.isInfoEnabled()) {  
  95.             logger.info("============ removeObject key: {}============", key);  
  96.         }  
  97.         String result = "success";  
  98.         Jedis jedis = null;  
  99.         try {  
  100.             jedis = RedisStandAloneUtil.getJedisPool().getResource();  
  101.             jedis.del(String.valueOf(key));  
  102.         } catch (Exception e) {  
  103.             logger.error(e.getMessage(), e);  
  104.         } finally {  
  105.             if (jedis != null) {  
  106.                 jedis.close();  
  107.             }  
  108.         }  
  109.         return result;  
  110.     }  
  111.     @Override  
  112.     public void clear() {  
  113.         if (logger.isInfoEnabled()) {  
  114.             logger.info("============ start clear cache ============");  
  115.         }  
  116.         String result = "fail";  
  117.         Jedis jedis = null;  
  118.         try {  
  119.             jedis = RedisStandAloneUtil.getJedisPool().getResource();  
  120.             result = jedis.flushAll();  
  121.         } catch (Exception e) {  
  122.             logger.error(e.getMessage(), e);  
  123.         } finally {  
  124.             if (jedis != null) {  
  125.                 jedis.close();  
  126.             }  
  127.         }  
  128.         if (logger.isInfoEnabled()) {  
  129.             logger.info("============ end clear cache result is {}============", result);  
  130.         }  
  131.     }  
  132.     @Override  
  133.     public ReadWriteLock getReadWriteLock() {  
  134.         return readWriteLock;  
  135.     }  
  136. }  

 

(3) mapper配置中加入自定义redis二级缓存:

 

Xml代码  收藏代码
  1. <cache type="com.sohu.tv.redis.MybatisRedisCache"/>  

(4) 单元测试同第二节