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/beans
10       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
11       http://www.springframework.org/schema/util
12       http://www.springframework.org/schema/util/spring-util-4.3.xsd
13       http://www.springframework.org/schema/mvc
14       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
15       http://www.springframework.org/schema/aop
16       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
17       http://www.springframework.org/schema/context
18       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 stub
145         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      @Autowired
11         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 cjl
11  *
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共享。

原文地址:https://www.cnblogs.com/cuijiale/p/8012008.html