sping整合redis,以及做mybatis的第三方缓存
一、spring整合redis
Redis作为一个时下非常流行的NOSQL语言,不学一下有点过意不去。
背景:学习Redis用到的框架是maven+spring+mybatis(框架如何搭建这边就不叙述了)
首先在pom里面添加当前所需要的jar包,有下面几个:
………………
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency> <!--mybaitis 缓存-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
<!-- spring data redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.1.RELEASE</version>
</dependency>
……………………
第一个是支持Redis的语言——Jedis包
第二个是依赖包
第三个是mybaitis自己做的一个 mybatis与redis整合的一个jar包
第四个是spring与redis整合的需要的jar包
先看spring与redis整合的配置文件,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--redis哨兵 -->
<!--<bean id="redisSentinelConfiguration"
class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="mymaster"></property>
</bean>
</property>
<property name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg index="0" value="${redis.host.slave}"/>
<constructor-arg index="1" value="${redis.port}"/>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg index="0" value="${redis.host.master}"/>
<constructor-arg index="1" value="${redis.port}"/>
</bean>
</set>
</property>
</bean>--> <bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.password}"/>
<property name="usePool" value="false"/>
<!--<property name="poolConfig" ref="poolConfig"/>-->
<!--<constructor-arg ref="redisSentinelConfiguration"/>-->
<property name="timeout" value="10000"/>
</bean> <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory"/>
</bean> <!-- 使用中间类解决RedisCache.jedisConnectionFactory的静态注入,从而使MyBatis实现第三方缓存 -->
<bean id="redisCacheTransfer" class="com.anhoo.util.RedisCacheTransfer">
<property name="jedisConnectionFactory" ref="jedisConnFactory"/>
</bean> </beans>
其中所涉及到的config.properties文件内容为:
redis.host=127.0.0.1
redis.port=7777
redis.password=eoooxy driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/anhoo
username=root
password=root
8-27行是配置redis集群需要的哨兵,这边暂且不讲,下次再说。
29-38是通过JedisConnectionFactory来配置redis的初始化配置。这里可以配置poolConfig,即jedis的进一步性能的配置,以及sentinel哨兵的配置,这里用到,下次再说
40-42通过jedis的配置,注入到StringRedisTemplate中,得到stringRedisTemplate bean
45-47是redis做mybatis的缓存的时候,需要的用到的,下面讲到的时候具体再说。
这样一来测试下,(这里在获取string-redis.xml中的stringRedisTemplate bean的时候,需要把对应的配置文件引入的配置放到当前文件中,或者把hostName、post、password,直接替换成具体的值)
@Test
public void redis() { Map<String, List<Map<String, String>>> hashMap = new HashMap<String, List<Map<String, String>>>();
Map<String, String> map = new HashMap<>();
map.put("name", "xueyuan");
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
list.add(map);
hashMap.put("hashName", list); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-redis.xml");
StringRedisTemplate template = ctx.getBean(StringRedisTemplate.class);
template.opsForValue().set("name", "eoooxy");
template.opsForHash().put("hash", "name", hashMap.toString());
System.out.println(template.opsForValue().get("name"));
System.out.println(template.opsForHash().get("hash", "name")); }
打印结果为:

在查看一下redis中的key,以及结果:

如果这些都成功的话,那么你的redis基本配置完成,下面只要在你的spring-content.xml 引入spring-redis.xml。每个人的这个文件名不一样,这个文件就是在web.xml的一个配置,如下:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
这样一来,如果在springmvc框架的service层需要用到redis的话,只需要自动注入bean就可以了,如下:
@Autowired
StringRedisTemplate stringRedisTemplate;
二、redis做mybatis的第三方缓存
mybatis自己已经做了一个兼容redis的第三方缓存的jar包,具体项目git连接: https://github.com/mybatis/redis-cache
官方描述使用jar的方法是:首先要在pom.xml中引入mybatis-redis 的jar包,前面我们已经引入了。之后只要在mybatis的Mapper中引入:
<mapper namespace="org.acme.FooMapper">
<cache type="org.mybatis.caches.redis.RedisCache" />
...
</mapper>
做好上面的操作,还需要我们在classpath 的resource下面放一个redis的配置文件:/redis.properties
下面就简单介绍下 mybatis-redis 的工作顺序,其主要有以下几个jar包:
mybatis-redis
|----DummyReadWriteLock
|----RedisCache
|----RedisCallback
|----RedisConfig
|----RedisConfigurationBuilder
|----SerializeUtil
我们在使用的时候,先后步骤为:
- 当调用了RedisCache类,RedisCache通过他的构造函数,使得RedisConfigurationBuilder通过 ./redis.properties 来创建redis的配置文件类RedisConfig
 - 这个时候RedisCache根据得到的RedisConfig,创建了PoolConfig
 - RedisCache的execute方法又根据PoolConfig得到了Jedis
 - 根据RedisCallBack回调Jedis做对应的get、put等等操作
 
但是在使用redis-cache的时候他有三个不足之处:
- 默认情况下,mybatis会为每一个mapper创建一个RedisCache,而JedisPool是在RedisCache的构造方法中创建的,这就意味着会为每一个mapper创建一个JedisPool,使用意图和开销上面都会有问题;
 - 在很多情况下,我们的应用中也会独立使用到redis,这样也无法让RedisCache直接使用我们项目中可能已经存在的JedisPool;并且会造成两个配置文件(除非我们应用也使用redis.properties);
 - RedisCache是使用hash来缓存一个Mapper中的查询,所以我们只能通过mybatis的cache配置来控制对象的生存时间,空闲时间等属性;而无法独立的去配置每一个缓存区域(即每一个hash);
 
下面针对上面的三个不足之处进行了进一步的改进:
需要用到mybatis-redis 中SerializeUtil类(这个工具类用起来还是很好用的 :) )以及 RedisCacheTransfer类,这个类的目的是根据其 setJedisConnectionFactory 方法自动注入方式得到spring-redis.xml中的 jedisConnFactory bean,再通过的set注入方式,注入到到自定义RedisCache中的 jedisConnectionFactory(这个也就是上面spring-redis.xml 45-47行的作用)
public class RedisCacheTransfer {
    @Autowired
    public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
        RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
    }
}
这样一来我们就解决了 上面第二不足之处,对于第一与第三个不足之处,因为redis-cache用的是hash来缓存Mapper中的查询,这里我们采用string数据结构来缓存。下面就根绝redis-chahe我们自己自定义一个RedisCache,又因自定义RedisCache中的jedisConnectionFactory是一个工厂模式,需要的时候就直接创建一个连接,这样一来就不存在创建多个poolConfig了。
import com.anhoo.common.BaseBean;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.exceptions.JedisConnectionException; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; /**
* Author XueYuan
* Data 2017/05/16
* Time 16:04
*/ public class RedisCache extends BaseBean implements Cache { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private String id;
private static JedisConnectionFactory jedisConnectionFactory; public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.debug("---------------------Mybatis RedisCache:id=" + id + "---------------------");
this.id = id;
} /**
* 清除所有数据
*/
@Override
public void clear() {
JedisConnection connection = null;
try {
connection = (JedisConnection) jedisConnectionFactory.getConnection();
connection.flushAll();
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
} /**
* @return
*/
@Override
public String getId() {
return this.id;
} /**
* 得到指定key的 value
* @param key
* @return object
*/
@Override
public Object getObject(Object key) {
Object result = null;
JedisConnection connection = null;
try {
connection = (JedisConnection) jedisConnectionFactory.getConnection();
result = SerializeUtil.unserialize(connection.get(SerializeUtil.serialize(key)));
//result = SerializeUtil.unserialize(connection.hGet(RedisCache.this.id.toString().getBytes(), key.toString().getBytes()));
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
} /**
* 得到当前db的key值
* @return int
*/
@Override
public int getSize() {
int result = 0;
JedisConnection connection = null;
try {
connection = (JedisConnection) jedisConnectionFactory.getConnection();
result = Integer.valueOf(connection.dbSize().toString());
// result = Integer.valueOf(connection.hGetAll(RedisCache.this.id.toString().getBytes()).size());
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
} /**
* 写入 key-value
* @param key
* @param value
*/
@Override
public void putObject(Object key, Object value) {
JedisConnection connection = null;
try {
logger.debug("------------------Redis Put Object:" + key.toString() + ":" + value.toString() + "-------------------");
connection = (JedisConnection) jedisConnectionFactory.getConnection();
connection.set(SerializeUtil.serialize(key), SerializeUtil.serialize(value));
// connection.hSet(RedisCache.this.id.toString().getBytes(),key.toString().getBytes(),SerializeUtil.serialize(value));
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
} /**
* 删除指定key的值
* @param key
* @return
*/
@Override
public Object removeObject(Object key) {
JedisConnection connection = null;
Object result = null;
try {
connection = (JedisConnection) jedisConnectionFactory.getConnection();
result = connection.expire(SerializeUtil.serialize(key), 0);
//或者 result = connection.del(SerializeUtil.serialize(key));
// result = connection.hDel(RedisCache.this.id.toString().getBytes(),key.toString().getBytes());
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
} @Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
} /**
* 注入jedisConnectionFactory
* @param jedisConnectionFactory
*/
public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.jedisConnectionFactory = jedisConnectionFactory;
}
}
其中每个方法下面都有被注释的存储到hash结构的方法,我们这里才用的是string,所以存储到redis中的是key-value对应的,这样一来我们就可以解决key的生存时间了。
这里面主要通过set注入的方法注入 jedisConnectionFactory 之后通过其做一系列的 get、put等等操作。
当然使用string结构来存储也有不足之处:会生成大量的key
上面都配置完毕之后,使用方法跟mybatis-redis一致,需要开启mybatis的缓存:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--<setting name="logImpl" value="LOG4J2" />--> <!-- 全局映射器启用缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
下面就做一个小小的测试 sql查询如下,其他具体的文件就不一一列出来了,如果需要的话,可以在git上面下载。
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.anhoo.mapper.UserEntityMapper"> <cache type="com.anhoo.util.RedisCache"/> <!--<cache type="org.mybatis.caches.redis.RedisCache"/>--> <select id="selectByUserName" parameterType="java.lang.String" resultType="com.anhoo.entity.UserEntity">
SELECT *
FROM user
WHERE username = #{userName,jdbcType=VARCHAR}
</select> </mapper>
当我们第一次查询的时候,控制台显示:

在service中打断点得到查询的值为:

第一个红框表示查询到了一条语句,第二个红框是把查询的key-value放到redis中,我们可以通过redis-cli来验证下,如下所示:

之后我们清除控制台信息,再次查询:断点的值与上面一致,但是控制台中不是从数据库中获取的,而是从缓存中获取,控制台信息如下:

本文涉及到的配置以及框架内容下载连接:https://github.com/eoooxy/anhoo 如有错误,谢谢指出。
参考:
http://www.jianshu.com/p/648c10420df1
http://www.cnblogs.com/springlight/p/6374372.html
http://www.cnblogs.com/yjmyzz/p/4105731.html
sping整合redis,以及做mybatis的第三方缓存的更多相关文章
- 使用Redis做MyBatis的二级缓存
		
使用Redis做MyBatis的二级缓存 通常为了减轻数据库的压力,我们会引入缓存.在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数据库了. 如果没有才去 ...
 - SpringBoot30 整合Mybatis-Plus、整合Redis、利用Ehcache实现二级缓存、利用SpringCache和Redis作为缓存
		
1 环境说明 JDK: 1.8 MAVEN: 3. SpringBoot: 2.0.4 2 SpringBoot集成Mybatis-Plus 2.1 创建SpringBoot 利用IDEA创建Spri ...
 - springboot 2.x整合redis,spring aop实现接口缓存
		
pox.xml: <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g ...
 - Mybatis整合Redis实现二级缓存
		
Mybatis集成ehcache . 为什么需要缓存 拉高程序的性能 . 什么样的数据需要缓存 很少被修改或根本不改的数据 业务场景比如:耗时较高的统计分析sql.电话账单查询sql等 . ehcac ...
 - Mybatis整合(Redis、Ehcache)实现二级缓存
		
目的: Mybatis整合Ehcache实现二级缓存 Mybatis整合Redis实现二级缓存 Mybatis整合ehcache实现二级缓存 ssm中整合ehcache 在POM中导入相关依赖 < ...
 - 八、springboot整合redis
		
整合Redis 一. 注解方式实现添加缓存 1.在pom.xml加入依赖 <!-- 配置使用redis启动器 --> <dependency> <groupId>o ...
 - redis可以做什么?
		
redis可以做什么? 1.缓存,毫无疑问这是Redis当今最为人熟知的使用场景.在提升服务器性能方面非常有效: 2.排行榜,如果使用传统的关系型数据库来做这个事儿,非常的麻烦,而利用Redis的So ...
 - SSM+redis整合(mybatis整合redis做二级缓存)
		
SSM:是Spring+Struts+Mybatis ,另外还使用了PageHelper 前言: 这里主要是利用redis去做mybatis的二级缓存,mybaits映射文件中所有的select都会刷 ...
 - Spring+SpringMVC+Mybatis整合redis
		
SSM整合redis redis是一种非关系型数据库,与mongoDB不同的是redis是内存数据库,所以访问速度很快.常用作缓存和发布-订阅式的消息队列. 这里用的是ssm框架+maven构建的项目 ...
 
随机推荐
- 【Django】【待解决问题】
			
1. from Crypto.Cipher import AES File "/Library/Frameworks/Python.framework/Versions/3.5/lib/py ...
 - Qt_2D_画图教程
			
1. ZC: 看点:相同的API,QPainter.QPainterDevice和QPainterEngine这3个类 Qt学习之2D绘图(画刷和画笔) http://blog.csdn.net/lp ...
 - [osg]osg自定义事件的理解
			
参考:http://blog.csdn.net/l_andy/article/details/51058907 添加自定义事件 首先osg在其内部通过osgGA::EventQueue类维护了一个事件 ...
 - Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks论文理解
			
一.创新点和解决的问题 创新点 设计Region Proposal Networks[RPN],利用CNN卷积操作后的特征图生成region proposals,代替了Selective Search ...
 - Flutter学习笔记(二)
			
*.assets 当引用图片的时候,需要在pubspec.yaml的文件中的flutter下添加assets,类似于下面的样子: image.png 这里需要注意的是文件里的assets只要一个缩进即 ...
 - eclipse Git配置
			
Git 1:选择git 2:下载对应版本 安装 Git常用命令: 显示信息类命令 git ls-files -u 显示冲突的文件,-s是显示标记为冲突已解决的文件 git diff 对比工作区和st ...
 - PHP自带调试函数
			
1.var_dump:打印变量的相关信息 $a = array(1, 2, array("a", "b", "c")); var_dump( ...
 - UEditor自动调节宽度
			
UEditor自动调节宽度 一.总结 一句话总结:ueditor是网页的产物,没有API我们照样可以像调网页元素那样调,一样的,这里是改变插件的css样式实现 启示: 百度 文档 引擎 ueditor ...
 - LeetCode--226--翻转二叉树
			
问题描述: 翻转一棵二叉树. 示例: 输入: 4 / \ 2 7 / \ / \ 1 3 6 9 输出: 4 / \ 7 2 / \ / \ 9 6 3 1 备注: 这个问题是受到 Max Howel ...
 - SQL 基础学习(2) Joining 和function , 作业没有做,需要看百宝箱。NOsql的概念
			
SQL 基础学习(2) Joining 可以同时关联(joining)多张表进行复杂的查询. 相比于用Rails捞出数据再用Ruby进行过滤组合,使用SQL更加高效,节能. 以下是 users has ...