redis整合spring mybatis --緩存方案

上一篇總結了redis sentinel(哨兵方案)的配置流程,本篇就redis整合ssm框架進行說明。目前,大多數公司用redis主要做緩存用,對於那些不常變動的數據來說,我們將其緩存在redis中,可以大大減少數據庫的壓力。

一、Spring集成redis

1.在resource目錄下創建spring-redis.xml文件,內容如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beansxmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
  6. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
  8.  
  9. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  10. <property name="maxIdle" value="2000" />
  11. <property name="maxTotal" value="20000" />
  12. <property name="minEvictableIdleTimeMillis" value="300000"></property>
  13. <property name="numTestsPerEvictionRun" value="3"></property>
  14. <property name="timeBetweenEvictionRunsMillis" value="60000"></property>
  15. <property name="maxWaitMillis" value="20000" />
  16. <property name="testOnBorrow" value="false" />
  17. </bean>
  18.  
  19. <bean id="sentinelConfig"
  20. class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  21. <property name="master">
  22. <bean class="org.springframework.data.redis.connection.RedisNode">
  23. <property name="name" value="mymaster"></property>
  24. </bean>
  25. </property>
  26. <property name="sentinels">
  27. <set>
  28. <bean class="org.springframework.data.redis.connection.RedisNode">
  29. <constructor-arg name="host" value="192.168.12.90" />
  30. <constructor-arg name="port" value="7505" />
  31. </bean>
  32. <bean class="org.springframework.data.redis.connection.RedisNode">
  33. <constructor-arg name="host" value="192.168.12.90" />
  34. <constructor-arg name="port" value="7506" />
  35. </bean>
  36. </set>
  37. </property>
  38. </bean>
  39. <!-- 在此將sentinel配置集成到redis連接池中 -->
  40. <bean id="jedisConnectionFactory"
  41. class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  42. <property name="timeout" value="20000"></property>
  43. <property name="poolConfig" ref="jedisPoolConfig"></property>
  44. <constructor-arg name="sentinelConfig" ref="sentinelConfig"></constructor-arg>
  45. </bean>
  46.  
  47. <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
  48. <property name="connectionFactory" ref="jedisConnectionFactory" />
  49. </bean>
  50.  
  51. </beans>

在上述配置中,我們用jedis 池化管理方案,將sentinel納入配置中去,這樣就不用代碼中用JedisSentinelPool了,直接用JedisPool就ok了。別忘了,將此文件加入web.xml中:

  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>classpath:spring-mybatis.xml,classpath:spring-redis.xml</param-value>
  4. </context-param>

2.測試

新建測試類,內容如下:

  1. package com.dg;
  2.  
  3. import java.util.Collection;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.Set;
  7.  
  8. import javax.annotation.Resource;
  9.  
  10. import org.junit.Test;
  11. import org.junit.runner.RunWith;
  12. import org.springframework.data.redis.connection.RedisSentinelConnection;
  13. import org.springframework.data.redis.connection.RedisServer;
  14. import org.springframework.data.redis.core.StringRedisTemplate;
  15. import org.springframework.test.context.ContextConfiguration;
  16. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  17.  
  18. import com.alibaba.fastjson.JSONObject;
  19. import com.dg.bean.User;
  20.  
  21. @RunWith(SpringJUnit4ClassRunner.class)
  22. @ContextConfiguration(locations = { "classpath:spring-redis.xml" })
  23. public class SpringRedis {
  24.  
  25. @Resource(name = "stringRedisTemplate")
  26. private StringRedisTemplate stringRedisTemplate;
  27.  
  28. /**
  29. * redis 讀寫測試
  30. */
  31. @Test
  32. public void testSpringRedis() {
  33.  
  34. try {
  35. // ApplicationContext context = new
  36. // ClassPathXmlApplicationContext("spring-redis.xml");
  37. // StringRedisTemplate stringRedisTemplate =
  38. // context.getBean("stringRedisTemplate",
  39. // StringRedisTemplate.class);
  40. // String讀寫
  41. stringRedisTemplate.delete("myStr");
  42. stringRedisTemplate.opsForValue().set("myStr", "http://yjmyzz.cnblogs.com/");
  43. System.out.println(stringRedisTemplate.opsForValue().get("myStr"));
  44. System.out.println("---------------");
  45.  
  46. // List讀寫
  47. stringRedisTemplate.delete("myList");
  48. stringRedisTemplate.opsForList().rightPush("myList", "A");
  49. stringRedisTemplate.opsForList().rightPush("myList", "B");
  50. stringRedisTemplate.opsForList().leftPush("myList", "0");
  51. List<String> listCache = stringRedisTemplate.opsForList().range("myList", 0, -1);
  52. for (String s : listCache) {
  53. System.out.println(s);
  54. }
  55. System.out.println("---------------");
  56.  
  57. // Set讀寫
  58. stringRedisTemplate.delete("mySet");
  59. stringRedisTemplate.opsForSet().add("mySet", "A");
  60. stringRedisTemplate.opsForSet().add("mySet", "B");
  61. stringRedisTemplate.opsForSet().add("mySet", "C");
  62. Set<String> setCache = stringRedisTemplate.opsForSet().members("mySet");
  63. for (String s : setCache) {
  64. System.out.println(s);
  65. }
  66. System.out.println("---------------");
  67.  
  68. // Hash讀寫
  69. stringRedisTemplate.delete("myHash");
  70. stringRedisTemplate.opsForHash().put("myHash", "PEK", "北京");
  71. stringRedisTemplate.opsForHash().put("myHash", "SHA", "上海虹橋");
  72. stringRedisTemplate.opsForHash().put("myHash", "PVG", "浦東");
  73. Map<Object, Object> hashCache = stringRedisTemplate.opsForHash().entries("myHash");
  74. for (Map.Entry<Object, Object> entry : hashCache.entrySet()) {
  75. System.out.println(entry.getKey() + " - " + entry.getValue());
  76. }
  77. System.out.println("---------------");
  78. } catch (Exception e) {
  79. e.printStackTrace();
  80. }
  81. }
  82.  
  83. /**
  84. * redis 得到所有的master and slave 信息
  85. */
  86. @Test
  87. public void testGetAllMasterAndSlave() {
  88.  
  89. RedisSentinelConnection conn = stringRedisTemplate.getConnectionFactory().getSentinelConnection();
  90.  
  91. for (RedisServer master : conn.masters()) {
  92. System.out.println("master => " + master);// 打印master信息
  93. Collection<RedisServer> slaves = conn.slaves(master);
  94. // 打印該master下的所有slave信息
  95. for (RedisServer slave : slaves) {
  96. System.out.println("slaves of " + master + " => " + slave);
  97. }
  98. System.out.println("--------------");
  99. }
  100. }
  101.  
  102. /*
  103. * 測試redis 緩存object 和 list 類型數據(方案:用json將object和list序列化)
  104. */
  105. @Test
  106. public void testRedisCacheObjectAndList() {
  107.  
  108. User user1 = new User("zhangsan", "123456", "[email protected]", "15824812342", 'M', 22);
  109. // // fastJson 序列化
  110. // String jsonStr = JSONObject.toJSONString(user1);
  111. // System.out.println(">>>>>>>>>>>>>>>>" + jsonStr);
  112. // // fastJson 反序列化
  113. // user1 = JSONObject.parseObject(jsonStr, User.class);
  114. // System.out.println(">>>>>>>>>>>>>>>>>>" + user1);
  115.  
  116. stringRedisTemplate.delete("user1");
  117. // 將object 用 json 序列化後保存redis
  118. stringRedisTemplate.opsForValue().set("user1", JSONObject.toJSONString(user1));
  119.  
  120. user1 = JSONObject.parseObject(stringRedisTemplate.opsForValue().get("user1"), User.class);
  121. System.out.println("-----------------------" + user1);
  122. }
  123. }
  124. /**測試redis客戶端*/
  125. @Test
  126. public void testRedis(){
  127.  
  128. Jedis jedis = new Jedis("192.168.12.90", 6379);
  129.  
  130. jedis.set("name", "mrdg");
  131. jedis.set("age", "24");
  132.  
  133. System.out.println("name:"+jedis.get("name"));
  134. System.out.println("age:"+jedis.get("age"));
  135. System.out.println("tel:"+jedis.get("Tel"));
  136. System.out.println("no:"+jedis.get("No"));
  137. }
  138. /**測試redis集羣方案*/
  139. @Test
  140. public void testCluster(){
  141.  
  142. Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
  143. //Jedis Cluster will attempt to discover cluster nodes automatically
  144. jedisClusterNodes.add(new HostAndPort("192.168.12.90", 7001));
  145. JedisCluster jc = new JedisCluster(jedisClusterNodes);
  146. jc.set("foo", "bar");
  147. String value = jc.get("foo");
  148.  
  149. System.out.println(value);
  150. try {
  151. jc.close();
  152. } catch (Exception e) {
  153. e.printStackTrace();
  154. }
  155. }

上面的測試類,cluster方案集羣環境爲(詳見http://blog.csdn.net/donggang1992/article/details/50981954):

這裏寫圖片描述


sentinel方案環境爲(詳見http://blog.csdn.net/donggang1992/article/details/50981341):

這裏寫圖片描述


二、mybatis集成redis進行緩存配置

1.mybatis開啓緩存支持

在spring-mabatis.xml中添加下列內容:

  1. <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
  2. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  3. <property name="dataSource" ref="dataSource" />
  4. <!-- 自動掃描mapping.xml文件 -->
  5. <property name="mapperLocations" value="classpath:com/dg/mapping/*.xml"></property>
  6. <!-- 開啓緩存支持 -->
  7. <property name="configurationProperties">
  8. <props>
  9. <prop key="cacheEnabled">true</prop>
  10. <!-- 查詢時,關閉關聯對象即時加載以提高性能 -->
  11. <prop key="lazyLoadingEnabled">false</prop>
  12. <!-- 設置關聯對象加載的形態,此處爲按需加載字段(加載字段由SQL指定),不會加載關聯表的所有字段,以提高性能 -->
  13. <prop key="aggressiveLazyLoading">true</prop>
  14. <!-- 對於未知的SQL查詢,允許返回不同的結果集以達到通用的效果 -->
  15. <prop key="multipleResultSetsEnabled">true</prop>
  16. <!-- 允許使用列標籤代替列名 -->
  17. <prop key="useColumnLabel">true</prop>
  18. <!-- 允許使用自定義的主鍵值(比如由程序生成的UUID 32位編碼作爲鍵值),數據表的PK生成策略將被覆蓋 -->
  19. <prop key="useGeneratedKeys">true</prop>
  20. <!-- 給予被嵌套的resultMap以字段-屬性的映射支持 -->
  21. <prop key="autoMappingBehavior">FULL</prop>
  22. <!-- 對於批量更新操作緩存SQL以提高性能 -->
  23. <prop key="defaultExecutorType">BATCH</prop>
  24. <!-- 數據庫超過25000秒仍未響應則超時 -->
  25. <prop key="defaultStatementTimeout">25000</prop>
  26. </props>
  27. </property>
  28. </bean>

2.新建cache包,並創建RedisCache類

RedisCache類的內容爲:

  1. package com.dg.cache;
  2.  
  3. import java.util.Set;
  4. import java.util.concurrent.locks.ReadWriteLock;
  5. import java.util.concurrent.locks.ReentrantReadWriteLock;
  6.  
  7. import org.apache.commons.codec.digest.DigestUtils;
  8. import org.apache.ibatis.cache.Cache;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11. import org.springframework.context.ApplicationContext;
  12. import org.springframework.context.support.ClassPathXmlApplicationContext;
  13.  
  14. import com.dg.util.SerializeUtil;
  15.  
  16. import redis.clients.jedis.Jedis;
  17. import redis.clients.jedis.JedisPool;
  18. import redis.clients.jedis.JedisPoolConfig;
  19.  
  20. /*
  21. * 使用第三方緩存服務器,處理二級緩存
  22. */
  23. publicclass RedisCache implements Cache {
  24.  
  25. privatestatic Logger logger = LoggerFactory.getLogger(RedisCache.class);
  26.  
  27. /** The ReadWriteLock. */
  28. privatefinal ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  29. private String id;
  30. private JedisPool jedisPool;
  31. privatestaticfinalint DB_INDEX = 1;
  32. privatefinal String COMMON_CACHE_KEY = "COM:";
  33. privatestaticfinal String UTF8 = "utf-8";
  34.  
  35. private ApplicationContext context;
  36.  
  37. /**
  38. * 按照一定規則標識key
  39. */
  40. private String getKey(Object key) {
  41. StringBuilder accum = new StringBuilder();
  42. accum.append(COMMON_CACHE_KEY);
  43. accum.append(this.id).append(":");
  44. accum.append(DigestUtils.md5Hex(String.valueOf(key)));
  45. return accum.toString();
  46. }
  47.  
  48. /**
  49. * redis key規則前綴
  50. */
  51. private String getKeys() {
  52. return COMMON_CACHE_KEY + this.id + ":*";
  53. }
  54.  
  55. publicRedisCache() {
  56.  
  57. }
  58.  
  59. publicRedisCache(final String id) {
  60. if (id == null) {
  61. thrownew IllegalArgumentException("必須傳入ID");
  62. }
  63. context = new ClassPathXmlApplicationContext("spring-redis.xml");
  64. JedisPoolConfig jedisPoolConfig = (JedisPoolConfig) context.getBean("jedisPoolConfig");
  65. jedisPool = new JedisPool(jedisPoolConfig, "192.168.12.90", 7504);
  66. logger.debug(">>>>>>>>>>>>>>>>>>>>>MybatisRedisCache:id=" + id);
  67. this.id = id;
  68. }
  69.  
  70. @Override
  71. public String getId() {
  72. returnthis.id;
  73. }
  74.  
  75. @Override
  76. publicintgetSize() {
  77. Jedis jedis = null;
  78. int result = 0;
  79. boolean borrowOrOprSuccess = true;
  80. try {
  81. jedis = jedisPool.getResource();
  82. jedis.select(DB_INDEX);
  83. Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF8));
  84. if (null != keys && !keys.isEmpty()) {
  85. result = keys.size();
  86. }
  87. logger.debug(this.id + "---->>>>總緩存數:" + result);
  88. } catch (Exception e) {
  89. borrowOrOprSuccess = false;
  90. if (jedis != null)
  91. jedisPool.returnBrokenResource(jedis);
  92. } finally {
  93. if (borrowOrOprSuccess)
  94. jedisPool.returnResource(jedis);
  95. }
  96. return result;
  97. }
  98.  
  99. @Override
  100. publicvoidputObject(Object key, Object value) {
  101. Jedis jedis = null;
  102. boolean borrowOrOprSuccess = true;
  103. try {
  104. jedis = jedisPool.getResource();
  105. jedis.select(DB_INDEX);
  106.  
  107. byte[] keys = getKey(key).getBytes(UTF8);
  108. jedis.set(keys, SerializeUtil.serialize(value));
  109. logger.debug("添加緩存--------" + this.id);
  110. // getSize();
  111. } catch (Exception e) {
  112. borrowOrOprSuccess = false;
  113. if (jedis != null)
  114. jedisPool.returnBrokenResource(jedis);
  115. } finally {
  116. if (borrowOrOprSuccess)
  117. jedisPool.returnResource(jedis);
  118. }
  119. }
  120.  
  121. @Override
  122. public Object getObject(Object key) {
  123. Jedis jedis = null;
  124. Object value = null;
  125. boolean borrowOrOprSuccess = true;
  126. try {
  127. jedis = jedisPool.getResource();
  128. jedis.select(DB_INDEX);
  129. value = SerializeUtil.unserialize(jedis.get(getKey(key).getBytes(UTF8)));
  130. logger.debug("從緩存中獲取-----" + this.id);
  131. // getSize();
  132. } catch (Exception e) {
  133. borrowOrOprSuccess = false;
  134. if (jedis != null)
  135. jedisPool.returnBrokenResource(jedis);
  136. } finally {
  137. if (borrowOrOprSuccess)
  138. jedisPool.returnResource(jedis);
  139. }
  140. return value;
  141. }
  142.  
  143. @Override
  144. public Object removeObject(Object key) {
  145. Jedis jedis = null;
  146. Object value = null;
  147. boolean borrowOrOprSuccess = true;
  148. try {
  149. jedis = jedisPool.getResource();
  150. jedis.select(DB_INDEX);
  151. value = jedis.del(getKey(key).getBytes(UTF8));
  152. logger.debug("LRU算法從緩存中移除-----" + this.id);
  153. // getSize();
  154. } catch (Exception e) {
  155. borrowOrOprSuccess = false;
  156. if (jedis != null)
  157. jedisPool.returnBrokenResource(jedis);
  158. } finally {
  159. if (borrowOrOprSuccess)
  160. jedisPool.returnResource(jedis);
  161. }
  162. return value;
  163. }
  164.  
  165. @Override
  166. publicvoidclear() {
  167. Jedis jedis = null;
  168. boolean borrowOrOprSuccess = true;
  169. try {
  170. jedis = jedisPool.getResource();
  171. jedis.select(DB_INDEX);
  172. // 如果有刪除操作,會影響到整個表中的數據,因此要清空一個mapper的緩存(一個mapper的不同數據操作對應不同的key)
  173. Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF8));
  174. logger.debug("出現CUD操作,清空對應Mapper緩存======>" + keys.size());
  175. for (byte[] key : keys) {
  176. jedis.del(key);
  177. }
  178. // 下面是網上流傳的方法,極大的降低系統性能,沒起到加入緩存應有的作用,這是不可取的。
  179. // jedis.flushDB();
  180. // jedis.flushAll();
  181. } catch (Exception e) {
  182. borrowOrOprSuccess = false;
  183. if (jedis != null)
  184. jedisPool.returnBrokenResource(jedis);
  185. } finally {
  186. if (borrowOrOprSuccess)
  187. jedisPool.returnResource(jedis);
  188. }
  189. }
  190.  
  191. @Override
  192. public ReadWriteLock getReadWriteLock() {
  193. return readWriteLock;
  194. }
  195. }

然後,在mapper文件中添加下列一行使對應的表添加緩存支持:

  1. <cache eviction="LRU"type="com.dg.cache.RedisCache" />

我是以訂單表數據作爲緩存目標的(通常我們只是對不常變動的數據進行緩存,如登陸用戶信息、系統業務配置信息等,這裏我用訂單表只是作爲範例),應用結構如下:

這裏寫圖片描述

3.測試

測試緩存的話,我們建一個頁面,然後查詢日期範圍的訂單列表,如下效果: 
這裏寫圖片描述

看輸出的日誌: 
這裏寫圖片描述

可以看出,初次查詢的時候,有sql語句,將訂單列表查詢出之後放入了緩存中去;返回來在執行剛纔的查詢動作時,只有下面的輸出,說明第二次沒再查庫,而是直接從redis緩存中去取。

這裏寫圖片描述

結果列表爲:

這裏寫圖片描述


以上只是簡單介紹了redis和ssm的整合過程,如果需要完整的代碼,到http://download.csdn.net/detail/donggang1992/9481612 下載。

發佈了27 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章