redis与ssm整合(用 redis 替代mybatis二级缓存)
SSM+redis整合
这里主要是利用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进行查询。