本文主要介绍将ssm项目与Redis进行整合,使用Redis作为缓存。其原理就是实现mybatis的自定义缓存,mybatis允许我们使用自定义的缓存来作为它的二级缓存,只需要实现它的Cache接口即可。
主要步骤如下:
0.ssm项目搭建(本文省略)
1.pom.xml里引入依赖
2.配置Redis的链接
3.自定义实现Cache
在这里可以根据自己的业务需要去实现cache。如:网上很多示例在clear时,使用flushDb(),这样会把所有缓存清除!而我只想删除相应mapper下的缓存,因此使用了list来存储和删除缓存,但是这样要注意在多表查询时会出现的脏数据等问题。
4.在mapper里开启二级缓存
1.pom.xml
加入Redis的依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.2.RELEASE</version>
</dependency>
2.Redis配置
在spring的配置文件里加入
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:redis.properties</value>
</list>
</property>
</bean>
<!-- redis数据源 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxActive}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- Spring-redis连接池管理工厂 -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"
p:pool-config-ref="poolConfig" />
<!-- 将jedisConnectionFactory 注入到自定义缓存中 -->
<bean id="RedisCache" class="cn.coolwind.redis.core.config.RedisCache">
<property name="jedisConnectionFactory" ref="jedisConnectionFactory" />
</bean>
redis.properties如下
# Redis settings
redis.host=localhost
redis.port=6379
redis.pass=
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
redis.testOnBorrow=true
3.自定义cache
在这里定义一个RedisCache类,根据自己的需要去实现Cache,分别是:
·String getId();//获取id
·void putObject(Object var1, Object var2);//写入缓存
·Object getObject(Object var1);//获取缓存
·Object removeObject(Object var1);//删除缓存
·void clear();//清空缓存
·int getSize();//获取大小
·ReadWriteLock getReadWriteLock();//获取ReadWriteLock
注意:
当进行insert、update操作时,会先去清空缓存,也就是调用clear方法,如果使用的是redisConnection.flushDb(),这样会把所有的缓存都清除,不符合我的需求。
我的方式:
因为每个namespace下的cache的id是不同的,每当调用putObject写入缓存时,可以使用一个list,将其key都存储在当前id的list下,当执行clear时,清除这个list下的缓存即可
具体代码如下:
public class RedisCache implements Cache {
@Autowired
private static JedisConnectionFactory jedisConnectionFactory;
private String id;
private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("id is null");
}
this.id = id;
}
public RedisCache() {
}
public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.jedisConnectionFactory = jedisConnectionFactory;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
JedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
connection.set(serializer.serialize(key), serializer.serialize(value));
connection.lPush(serializer.serialize(id),serializer.serialize(key));
System.out.println("写入缓存:" + key + "," + value);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
}
@Override
public Object getObject(Object key) {
Object res = null;
JedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
res = serializer.deserialize(connection.get(serializer.serialize(key)));
if (res != null) {
System.out.println("获取缓存数据:" + res.toString());
} else {
System.out.println("当前没有缓存:" + key);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return res;
}
@Override
public Object removeObject(Object key) {
Object res = null;
JedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
res = connection.expire(serializer.serialize(key), 0);
connection.lRem(serializer.serialize(id),0,serializer.serialize(key));
System.out.println("删除缓存:" + key);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return res;
}
@Override
public void clear() {
JedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
// connection.flushDb();
// connection.flushAll();
Long length = connection.lLen(serializer.serialize(id));
if (0 == length) {
return;
}
List<byte[]> keys = connection.lRange(serializer.serialize(id),0,length-1);
for (byte[] key :keys) {
connection.expireAt(key,0);
System.out.println("删除缓存:"+serializer.deserialize(key).toString());
}
connection.expireAt(serializer.serialize(id),0);
keys.clear();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
}
@Override
public int getSize() {
int result = 0;
JedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
result = Integer.valueOf(connection.dbSize().toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
}
@Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}
}
4.开启二级缓存
在mapper里加入:
<cache eviction="LRU" type="cn.coolwind.redis.core.config.RedisCache"/>
测试
准备了两个类:Test和Test2,分别对应两个不同的namespace
做如下操作:
1.分别查询Test和Test2
2.再次查询Test和Test2
3.对Test做修改操作
4.查询Test和Test2
得到如下打印结果:
从上面的打印结果可以看到:
- 当第一次查询的时候,没有缓存,会往Redis写入缓存
- 再次进行查询的时候,直接获取缓存数据
- 对Test进行修改时,会删除Test的缓存
- 再次查询的时候,Test会重新写入缓存,而Test2仍然在缓存中
这样清空缓存的作用域就是所在的namespace了;
如果使用redisConnection.flushDb()的话,有修改的时候Test和Test2的缓存都会被删除;
但是尽量不要使用多表操作,避免脏数据等问题。