03.redis+ssm整合(mybatis二级缓存)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了03.redis+ssm整合(mybatis二级缓存)相关的知识,希望对你有一定的参考价值。

SSM+redis整合

ssm框架之前已经搭建过了,这里不再做代码复制工作。

这里主要是利用redis去做mybatis的二级缓存,mybaits映射文件中所有的select都会刷新已有缓存,如果不存在就会新建缓存,所有的insert,update操作都会更新缓存。

redis的好处也显而易见,可以使系统的数据访问性能更高。本节只是展示了整合方法和效果,后面会补齐redis集群、负载均衡和session共享的文章。

下面就开始整合工作:

技术分享图片

后台首先启动redis-server(后台启动与远程连接linux服务的方法都需要改redis.conf文件),启动命令“./src/redis-server ./redis.conf”

我这里是windows系统下开发的,推荐一个可视化工具“Redis Desktop manager”,需要远程连接linux下的redis,需要linux下开启端口对外开放(具体方法是修改/etc/sysconfig/iptables文件,增加对外端口开发命令)。

以上操作都完成后,即可远程连接成功了,如图:

技术分享图片

技术分享图片

现在还没有缓存记录,下面进入代码阶段,首先在pom.xml中增加需要的redis jar包

 1 <dependency> 2             <groupId>redis.clients</groupId> 3             <artifactId>jedis</artifactId> 4             <version>2.9.0</version> 5         </dependency> 6          7         <dependency> 8             <groupId>org.springframework.data</groupId> 9             <artifactId>spring-data-redis</artifactId>10             <version>1.6.2.RELEASE</version>11         </dependency>12         13         <dependency>14             <groupId>org.mybatis</groupId>15             <artifactId>mybatis-ehcache</artifactId>16             <version>1.0.0</version>17         </dependency>18           <!-- 添加druid连接池包 -->19         <dependency>20             <groupId>com.alibaba</groupId>21             <artifactId>druid</artifactId>22             <version>1.0.24</version>23         </dependency>

pom.xml写好后,还需要新增两个配置文件:redis.properties

redis.host=192.168.0.109
redis.port=6379
redis.pass=123456
redis.maxIdle=200
redis.maxActive=1024
redis.maxWait=10000
redis.testOnBorrow=true

其中字段也都很好理解,再加入配置文件:spring-redis.xml

 1 <beans xmlns="http://www.springframework.org/schema/beans" 2   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3   xmlns:p="http://www.springframework.org/schema/p" 4   xmlns:mvc="http://www.springframework.org/schema/mvc" 5   xmlns:util="http://www.springframework.org/schema/util" 6   xmlns:aop="http://www.springframework.org/schema/aop" 7   xmlns:context="http://www.springframework.org/schema/context" 8   xmlns:task="http://www.springframework.org/schema/task"  9   xsi:schemaLocation="http://www.springframework.org/schema/beans10       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd11       http://www.springframework.org/schema/util12       http://www.springframework.org/schema/util/spring-util-4.3.xsd13       http://www.springframework.org/schema/mvc14       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd15       http://www.springframework.org/schema/aop16       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd17       http://www.springframework.org/schema/context18       http://www.springframework.org/schema/context/spring-context-4.3.xsd">19       20       21     <!-- 连接池基本参数配置,类似数据库连接池 -->22      <context:property-placeholder location="classpath*:redis.properties" />23      24     <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">25         <property name="maxTotal" value="${redis.maxActive}"/>26         <property name="maxIdle" value="${redis.maxIdle}" />27         <property name="testOnBorrow" value="${redis.testOnBorrow}"/>28     </bean>29     30     <!-- 连接池配置,类似数据库连接池 -->31     <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >32         <property name="hostName" value="${redis.host}"></property>33         <property name="port" value="${redis.port}"></property>34         <property name="password" value="${redis.pass}"></property>35         <property name="poolConfig"  ref="poolConfig"></property> 36     </bean>37     38     <!-- 调用连接池工厂配置 -->39     <!-- <bean id="redisTemplate" class=" org.springframework.data.redis.core.RedisTemplate">40         <property name="jedisConnectionFactory" ref="jedisConnectionFactory"></property>41         42         如果不配置Serializer,那么存储的时候智能使用String,如果用User类型存储,那么会提示错误User can't cast  to String!!!  
43          <property name="keySerializer">  
44             <bean  
45             class="org.springframework.data.redis.serializer.StringRedisSerializer" />  
46         </property>  
47         <property name="valueSerializer">  
48             <bean  
49                 class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />  
50         </property> 
51     </bean> -->52     <bean id="redisCacheTransfer" class="com.cjl.util.RedisCacheTransfer">53         <property name="jedisConnectionFactory" ref="jedisConnectionFactory" />54     </bean>55 </beans>

配置文件写好后,就开始java代码的编写:

JedisClusterFactory.java

  1 package com.cjl.util;  2   3 import java.util.HashSet;  4 import java.util.Properties;  5 import java.util.Set;  6 import java.util.regex.Pattern;  7   8 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;  9 import org.springframework.beans.factory.FactoryBean; 10 import org.springframework.beans.factory.InitializingBean; 11 import org.springframework.core.io.Resource; 12  13 import redis.clients.jedis.HostAndPort; 14 import redis.clients.jedis.JedisCluster; 15  16 public class JedisClusterFactory implements FactoryBean<JedisCluster>, InitializingBean { 17  18     private Resource addressConfig; 19     private String addressKeyPrefix; 20  21     private JedisCluster jedisCluster; 22     private Integer timeout; 23     private Integer maxRedirections; 24     private GenericObjectPoolConfig genericObjectPoolConfig; 25  26     private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$"); 27  28     public JedisCluster getObject() throws Exception { 29         return jedisCluster; 30     } 31  32     public Class<? extends JedisCluster> getObjectType() { 33         return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class); 34     } 35  36     public boolean isSingleton() { 37         return true; 38     } 39  40     private Set<HostAndPort> parseHostAndPort() throws Exception { 41         try { 42             Properties prop = new Properties(); 43             prop.load(this.addressConfig.getInputStream()); 44  45             Set<HostAndPort> haps = new HashSet<HostAndPort>(); 46             for (Object key : prop.keySet()) { 47  48                 if (!((String) key).startsWith(addressKeyPrefix)) { 49                     continue; 50                 } 51  52                 String val = (String) prop.get(key); 53  54                 boolean isIpPort = p.matcher(val).matches(); 55  56                 if (!isIpPort) { 57                     throw new IllegalArgumentException("ip 或 port 不合法"); 58                 } 59                 String[] ipAndPort = val.split(":"); 60  61                 HostAndPort hap = new HostAndPort(ipAndPort[0], Integer.parseInt(ipAndPort[1])); 62                 haps.add(hap); 63             } 64  65             return haps; 66         } catch (IllegalArgumentException ex) { 67             throw ex; 68         } catch (Exception ex) { 69             throw new Exception("解析 jedis 配置文件失败", ex); 70         } 71     } 72  73     public void afterPropertiesSet() throws Exception { 74         Set<HostAndPort> haps = this.parseHostAndPort(); 75  76         jedisCluster = new JedisCluster(haps, timeout, maxRedirections, genericObjectPoolConfig); 77  78     } 79  80     public void setAddressConfig(Resource addressConfig) { 81         this.addressConfig = addressConfig; 82     } 83  84     public void setTimeout(int timeout) { 85         this.timeout = timeout; 86     } 87  88     public void setMaxRedirections(int maxRedirections) { 89         this.maxRedirections = maxRedirections; 90     } 91  92     public void setAddressKeyPrefix(String addressKeyPrefix) { 93         this.addressKeyPrefix = addressKeyPrefix; 94     } 95  96     public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) { 97         this.genericObjectPoolConfig = genericObjectPoolConfig; 98     } 99 100 }

RedisCache.java

  1 package com.cjl.util;  2   3 import java.util.concurrent.locks.ReadWriteLock;  4 import java.util.concurrent.locks.ReentrantReadWriteLock;  5   6 import org.apache.ibatis.cache.Cache;  7 import org.slf4j.Logger;  8 import org.slf4j.LoggerFactory;  9 import org.springframework.data.redis.connection.jedis.JedisConnection; 10 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 11 import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; 12 import org.springframework.data.redis.serializer.RedisSerializer; 13  14 import redis.clients.jedis.exceptions.JedisConnectionException; 15  16 public class RedisCache implements Cache { 17     private static final Logger logger = LoggerFactory.getLogger(RedisCache.class); 18  19     private static JedisConnectionFactory jedisConnectionFactory; 20  21     private final String id; 22  23     private final ReadWriteLock rwl = new ReentrantReadWriteLock(); 24      25  26     public RedisCache(final String id) { 27         if (id == null) { 28             throw new IllegalArgumentException("Cache instances require an ID"); 29         } 30         logger.debug("MybatisRedisCache:id=" + id); 31         this.id = id; 32     } 33  34     /** 35      * 清空所有缓存 36      */ 37     public void clear() { 38         rwl.readLock().lock(); 39         JedisConnection connection = null; 40         try { 41             connection = jedisConnectionFactory.getConnection(); 42             connection.flushDb(); 43             connection.flushAll(); 44         } catch (JedisConnectionException e) { 45             e.printStackTrace(); 46         } finally { 47             if (connection != null) { 48                 connection.close(); 49             } 50             rwl.readLock().unlock(); 51         } 52     } 53  54     public String getId() { 55         return this.id; 56     } 57  58     /** 59      * 获取缓存总数量 60      */ 61     public int getSize() { 62         int result = 0; 63         JedisConnection connection = null; 64         try { 65             connection = jedisConnectionFactory.getConnection(); 66             result = Integer.valueOf(connection.dbSize().toString()); 67             logger.info("添加mybaits二级缓存数量:" + result); 68         } catch (JedisConnectionException e) { 69             e.printStackTrace(); 70         } finally { 71             if (connection != null) { 72                 connection.close(); 73             } 74         } 75         return result; 76     } 77  78     public void putObject(Object key, Object value) { 79         rwl.writeLock().lock(); 80  81         JedisConnection connection = null; 82         try { 83             connection = jedisConnectionFactory.getConnection(); 84             RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer(); 85             connection.set(SerializeUtil.serialize(key), SerializeUtil.serialize(value)); 86             logger.info("添加mybaits二级缓存key=" + key + ",value=" + value); 87         } catch (JedisConnectionException e) { 88             e.printStackTrace(); 89         } finally { 90             if (connection != null) { 91                 connection.close(); 92             } 93             rwl.writeLock().unlock(); 94         } 95     } 96  97     public Object getObject(Object key) { 98         // 先从缓存中去取数据,先加上读锁 99         rwl.readLock().lock();100         Object result = null;101         JedisConnection connection = null;102         try {103             connection = jedisConnectionFactory.getConnection();104             RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();105             result = serializer.deserialize(connection.get(serializer.serialize(key)));106             logger.info("命中mybaits二级缓存,value=" + result);107 108         } catch (JedisConnectionException e) {109             e.printStackTrace();110         } finally {111             if (connection != null) {112                 connection.close();113             }114             rwl.readLock().unlock();115         }116         return result;117     }118 119     public Object removeObject(Object key) {120         rwl.writeLock().lock();121 122         JedisConnection connection = null;123         Object result = null;124         try {125             connection = jedisConnectionFactory.getConnection();126             RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();127             result = connection.expire(serializer.serialize(key), 0);128         } catch (JedisConnectionException e) {129             e.printStackTrace();130         } finally {131             if (connection != null) {132                 connection.close();133             }134             rwl.writeLock().unlock();135         }136         return result;137     }138 139     public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {140         RedisCache.jedisConnectionFactory = jedisConnectionFactory;141     }142 143     public ReadWriteLock getReadWriteLock() {144         // TODO Auto-generated method stub145         return rwl;146     }147 148 }

RedisCacheTransfer.java

 1 package com.cjl.util; 2  3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 5  6 /** 7  * 静态注入中间类 8  */ 9 public class RedisCacheTransfer {10      @Autowired11         public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {12             RedisCache.setJedisConnectionFactory(jedisConnectionFactory);13         }14 15 }

SerializeUtil.java

 1 package com.cjl.util; 2  3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7  8 /** 9  * 
10  * @author cjl11  *12  */13 public class SerializeUtil {14     /**15      * 序列化16      */17     public static byte[] serialize(Object object) {18         ObjectOutputStream oos = null;19         ByteArrayOutputStream baos = null;20         try {21             // 序列化22             baos = new ByteArrayOutputStream();23             oos = new ObjectOutputStream(baos);24             oos.writeObject(object);25             byte[] bytes = baos.toByteArray();26             return bytes;27         } catch (Exception e) {28             e.printStackTrace();29         }30         return null;31     }32 33     /**34      *反序列化35      */36     public static Object unserialize(byte[] bytes) {37         if (bytes !=null) {38             ByteArrayInputStream bais = null;39             try {40                 // 反序列化41                 bais = new ByteArrayInputStream(bytes);42                 ObjectInputStream ois = new ObjectInputStream(bais);43                 return ois.readObject();44             } catch (Exception e) {45 46             }47         } 
48         return null;49     }50 }

所有东西准备齐全后还需要修改映射文件

技术分享图片

要使mybaits缓存生效,还需如上图这样开启二级缓存。配置文件还需要在web.xml中加载生效

技术分享图片

一切准备就绪后,启动服务

 技术分享图片

启动成功后,点击员工表单可以触发查询所有员工的方法,第一次进行查询语句可以看到mybatis打印了查询语句,并在redis服务器中更新了一条缓存

技术分享图片

技术分享图片

我们清空控制台再次点击查询员工按钮执行查询方法,可以看到没有执行查询语句,证明第二次查询直接从缓存中取值,没有连接mysql进行查询。

技术分享图片

以上整合基本已经完成,如有不对的地方希望大家能够指出,后面会补上负载均衡和session共享。

 


以上是关于03.redis+ssm整合(mybatis二级缓存)的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis的二级缓存讲解

MyBatis的二级缓存讲解

MyBatis的二级缓存讲解

SSM-MyBatis-17:Mybatis中二级缓存

SSM整合:Spring整合Mybatis

SpringBoot——整合SSM(主要整合MyBatis)