Java中使用Redis
四、Redis命令1. key的操作在redis里面,除了”\n”和空格 不能作为名字的组成部分,其他字符都可以作为key的名字的组成部分。名称的长度不限制。常见命令列表:命令解释exists key测试指定key是否存在del key1 key2 … keyN删除给定的keytype key返回给定key的value类型keys pattern返回匹配指定模式的所有keyrename oldke
七、Java中使用Redis
1. 选择客户端
选择java连接redisd的客户端工具包为jedis
2. 引入相关jar包
- jedis-2.9.0.jar
- commons-pool2-2.4.2.jar
maven的依赖配置
<!-- jedis的依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
3. 测试Jedis
public class JedisDemo {
public static void main(String[] args) {
//创建连接redis数据库的对象
// Jedis jedis = new Jedis("localhost", 6379);
//创建连接池对象
JedisPool jedisPool = new JedisPool("localhost", 6379);
//通过连接池对象获取连接redis的连接对象
Jedis jedis = jedisPool.getResource();
//测试是否连接
String pong = jedis.ping();
System.out.println("是否连接:" + pong);
//切换数据库
String result = jedis.select(1);
System.out.println("切换数据库:" + result);
//查看当前数据库中所有键key
Set<String> keys = jedis.keys("*");
System.out.println("所有键:" + keys);
//获取键的数量
long keySize = jedis.dbSize();
System.out.println("键的数量:" + keySize);
System.out.println("---string类型---");
//1.string类型
//存储
jedis.set("hello", "welcome to redis");
//获取
System.out.println(jedis.get("hello"));
System.out.println("---list类型---");
//2.list类型
//存储
jedis.lpush("names","张三","李四","王五");
//获取
List<String> names = jedis.lrange("names",0,100);
for (String name : names) {
System.out.println(name);
}
System.out.println("---set类型---");
//3.set类型
//存储
long setSize = jedis.sadd("city","北京","上海","深圳","上海");
System.out.println(setSize);
//获取
Set<String> city = jedis.smembers("city");
for (String s : city) {
System.out.println(s);
}
System.out.println("---sorted set类型---");
//4.sorted set类型
//存储
jedis.zadd("week",1,"周一");
jedis.zadd("week",2,"周二");
jedis.zadd("week",3,"周三");
//获取
Set<String> week = jedis.zrange("week", 0, 100);
for (String s : week) {
System.out.println(s);
}
Set<String> week2 = jedis.zrevrange("week",0,100);
for (String s : week2) {
System.out.println(s);
}
Map<String,Double> majorMap = new HashMap<>();
majorMap.put("软件工程",100.0);
majorMap.put("通信工程",80.0);
majorMap.put("网络工程",90.0);
jedis.zadd("major",majorMap);
Set<String> major = jedis.zrange("major",0,100);
for (String s : major) {
System.out.println(s);
}
System.out.println("---hash类型---");
//5. hash类型
jedis.hset("user1","id","101");
jedis.hset("user1","name","小明");
jedis.hset("user1","age","20");
Map<String,String> userMap = new HashMap<>();
userMap.put("id","102");
userMap.put("name","小红");
userMap.put("age","22");
jedis.hmset("user2", userMap);
List<String> user1 = jedis.hmget("user1","id","name","age");
for (String s : user1) {
System.out.println(s);
}
Map<String,String> user2 = jedis.hgetAll("user2");
System.out.println(user2);
}
}
八、Spring整合Redis
1. 引入相关jar包
相关jar包包含三部分:
- Spring的jar包
- jedis的jar包
- Spring对redis的支持jar包
maven的依赖配置
<!-- spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springbean包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springcontext包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- spring表达式包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springAOP包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring对redis的支持包-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.13.RELEASE</version>
</dependency>
<!-- jedis的依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
2. 配置redis
applicationContext-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<context:property-placeholder location="classpath:redis.properties"/>
<context:component-scan base-package="com.redis"/>
<!-- 配置redis数据源 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大空闲数 -->
<property name="maxIdle" value="${redis.maxIdle}"/>
<!-- 最大空连接数 -->
<property name="maxTotal" value="${redis.maxTotal}"/>
<!-- 最大等待时间 -->
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
<!-- 返回连接时,检测连接是否成功 -->
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<!-- 配置redis连接池管理工厂JedisConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!-- IP地址 -->
<property name="hostName" value="${redis.host}"/>
<!-- 端口号 -->
<property name="port" value="${redis.port}"/>
<!-- 密码 -->
<!--<property name="password" value="${redis.password}"/>-->
<!-- 超时时间 默认2000-->
<property name="timeout" value="${redis.timeout}" />
<!-- 选用的数据库 -->
<property name="database" value="${redis.database}"/>
<!-- 连接池配置引用 -->
<property name="poolConfig" ref="poolConfig"/>
</bean>
<!-- 配置RedisTemplate:spring中提供的redis操作工具类 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<!--注入连接工厂-->
<property name="connectionFactory" ref="jedisConnectionFactory"/>
</bean>
</beans>
redis.properties
# redis服务器地址
redis.host=localhost
# redis服务器端口号
redis.port=6379
# redis密码
#redis.password=123456
# redis超时时间
redis.timeout=2000
# redis选用的数据库
redis.database=1
# redis最大空闲数
redis.maxIdle=300
# redis最大空连接数
redis.maxTotal=600
# redis最大等待时间
redis.maxWaitMillis=1000
# reids返回连接时,检测连接是否成功
redis.testOnBorrow=true
# redis缓存数据过期时间单位秒
redis.expiration=0
3. RedisTemplate的配置与使用
3.1 Jedis与RedisTemplate的区别
Jedis是Redis官方推荐的面向Java的操作Redis的客户端,可以用JedisPool来获得连接进行get、set、del等操作相对简单,而RedisTemplate是SpringDataRedis中对JedisApi的高度封装。
3.2 测试
public class RedisTemplateDemo {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-redis.xml");
RedisTemplate redisTemplate = ac.getBean("redisTemplate", RedisTemplate.class);
System.out.println(redisTemplate);
redisTemplate.opsForValue().set("spring", "hello spring-reids");
System.out.println("存储成功...");
String key = (String) redisTemplate.opsForValue().get("hello");
System.out.println(key);
}
}
执行后输出如下:
可以看到存储和获取数据都没有问题,但是使用redis-cli,却查询不到 spring 这个key的内容。
使用代码去查没问题,直接控制台连接,发现这个key和我们预期的不一样,多了一些前缀。为什么呢?
3.3 序列化问题
为了解决上面的问题,查看RedisTemplate
的代码如下:
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
private boolean enableTransactionSupport = false;
private boolean exposeConnection = false;
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
private RedisSerializer<?> defaultSerializer;
private ClassLoader classLoader;
private RedisSerializer keySerializer = null;
private RedisSerializer valueSerializer = null;
private RedisSerializer hashKeySerializer = null;
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = new StringRedisSerializer();
private ScriptExecutor<K> scriptExecutor;
// cache singleton objects (where possible)
private ValueOperations<K, V> valueOps;
private ListOperations<K, V> listOps;
private SetOperations<K, V> setOps;
private ZSetOperations<K, V> zSetOps;
private GeoOperations<K, V> geoOps;
private HyperLogLogOperations<K, V> hllOps;
/**
* Constructs a new <code>RedisTemplate</code> instance.
*/
public RedisTemplate() {}
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<K>(this);
}
initialized = true;
}
//部分代码省略...
}
得知RedisTemplate中key/value的数据默认采用JdkSerializationRedisSerializer来进行序列化。
- keySerializer:字符串、列表、集合、有序集合、哈希的键的序列化策略
- valueSerializer:字符串、列表、集合、有序集合、哈希的值的序列化策略
- hashKeySerializer:哈希的值中数据键的序列化策略
- hashValueSerializer:哈希的值中数据值的序列化策略
而我们继续进一步查看源码,最后发现实现序列化的代码如下:
public class DefaultSerializer implements Serializer<Object> {
/**
* Writes the source object to an output stream using Java serialization.
* The source object must implement {@link Serializable}.
* @see ObjectOutputStream#writeObject(Object)
*/
@Override
public void serialize(Object object, OutputStream outputStream) throws IOException {
if (!(object instanceof Serializable)) {
throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +
"but received an object of type [" + object.getClass().getName() + "]");
}
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(object);
objectOutputStream.flush();
}
}
所以具体的实现很清晰了,就是ObjectOutputStream这个流,是Java中最原始的序列化反序列流工具,会包含类型信息,所以会带上那串前缀了。
3.4 序列化策略以及配置
针对数据的“序列化/反序列化”,提供了多种可选择策略:
- JdkSerializationRedisSerializer:序列化java对象,使用JDK本身序列化机制,将对象通过ObjectInputStream/ObjectOutputStream进行序列化操作,最终redis-server中将存储字节序列。目前默认的序列化策略。
- StringRedisSerializer:简单的字符串序列化,Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。
- Jackson2JsonRedisSerializer:序列化object对象为json字符串,jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定Class类型,因此此策略封装起来稍微复杂。【需要jackson工具支持】
- OxmSerializer:提供了将javabean与xml之间的转换能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存储的数据将是xml工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要spring-oxm模块的支持】
applicationContext.xml中修改redisTemplate的配置,为其注入序列化策略:
<!-- 配置RedisTemplate:spring中提供的redis操作工具类 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<!--注入连接工厂-->
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean>
配置之后再次测试,正常。
3.5 RedisTemplate API
针对不同的数据结构(String, List, Set, ZSet, Hash)封装了不同的调用方式 opsForXXX
- string类型:redisTemplate.opsForValue()
- list类型:redisTemplate.opsForList()
- set类型:redisTemplate.opsForSet()
- zset类型:redisTemplate.opsForZSet()
- hash类型:edisTemplate.opsForHash()
封装的工具类RedisUtil代码如下:
/**
* RedisTemplate 工具类
*/
@Component
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 递增
*
* @param key 键
* @param by 要增加几(大于0)
* @return
*/
public long incr(String key, long by) {
if (by < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, by);
}
/**
* 递减
*
* @param key 键
* @param by 要减少几(小于0)
* @return
*/
public long decr(String key, long by) {
if (by < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -by);
}
// ================================Hash=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
System.out.println("redis error: "+e);
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
System.out.println("redis error: "+e);
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
System.out.println("redis error: "+e);
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
System.out.println("redis error: "+e);
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
System.out.println("redis error: "+e);
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
System.out.println("redis error: "+e);
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
System.out.println("redis error: "+e);
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
System.out.println("redis error: "+e);
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
System.out.println("redis error: "+e);
return 0;
}
}
// ===============================sorted set=================================
/**
* 向有序集合添加一个成员的
*
* ZADD key score1 member1 [score2 member2]
*
*/
public boolean zadd(String key, Object member, double score, long time) {
try {
redisTemplate.opsForZSet().add(key, member, score);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
System.out.println("redis error: "+e);
return false;
}
}
/**
* ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT]
通过分数返回有序集合指定区间内的成员
*
*/
public Set<Object> zRangeByScore(String key, double minScore, double maxScore) {
try {
return redisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore);
} catch (Exception e) {
System.out.println("redis error: "+e);
return null;
}
}
/**
* ZSCORE key member
返回有序集中,成员的分数值
*
*/
public Double zscore(String key, Object member) {
try {
return redisTemplate.opsForZSet().score(key, member);
} catch (Exception e) {
System.out.println("redis error: "+e);
return null;
}
}
/**
* ZRANK key member 返回有序集合中指定成员的索引
*
*/
public Long zrank(String key, Object member) {
try {
return redisTemplate.opsForZSet().rank(key, member);
} catch (Exception e) {
System.out.println("redis error: "+e);
return null;
}
}
/**
* Zscan 迭代有序集合中的元素(包括元素成员和元素分值)
*
*/
public Cursor<ZSetOperations.TypedTuple<Object>> zscan(String key) {
try {
Cursor<ZSetOperations.TypedTuple<Object>> cursor = redisTemplate.opsForZSet().scan(key, ScanOptions.NONE);
return cursor;
} catch (Exception e) {
System.out.println("redis error: "+e);
return null;
}
}
}
3.6 StringRedisTemplate与RedisTemplate
- 两者的关系是StringRedisTemplate继承RedisTemplate。
- 两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。
- StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
- RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
4. 缓存注解
在实际应用中除了可以使用自己封装的util工具类,也可以通过注解的方式使用redis,实现数据缓存。
4.1 配置
添加命名空间和标签约束
xmlns:cache="http://www.springframework.org/schema/cache"
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
applicationContext.xml
<!-- 配置RedisCacheManager -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<!-- 注入redisTemplate实例 -->
<constructor-arg name="redisOperations" ref="redisTemplate"/>
<!-- 配置默认的key过期时间 -->
<property name="defaultExpiration" value="${redis.expiration}"/>
</bean>
<!-- 开启缓存注解 -->
<cache:annotation-driven/>
4.2 注解介绍
1、@Cacheable:配置在方法或类上,作用:在调用方法之前,首先应该在缓存中查找方法的返回值。如果这个值能够找到,就会返回缓存值。否则,方法就会被调用,返回值会放到缓存之中。
2、@CachePut:配置在方法或类上,作用:将方法的返回值放到缓存中。在方法的调用前不会检查缓存,方法始终都会被调用。
@Cacheable和@CachePut共有参数如下:
参数名称 | 类型 | 解析 | 示例 |
---|---|---|---|
value | String[] | 要使用的缓存名称 | @Cacheable(value=“user:loginUser”) @Cacheable(value={“user:loginUser”,“myCache”} |
key | String | SpEL表达式,缓存的key,可以为空 | @Cacheable(value=“user:loginUser”,key="#username") |
condition | String | SpEL表达式,缓存的条件,可以为空,只有为true才进行缓存 | @Cacheable(value=“user:loginUser”, key="#username", condition="#username.length()>2") |
unless | String | SpEL表达式,缓存的条件,与condition相反,只有为false才进行缓存 | @Cacheable(value=“user:loginUser”, key="#username", unless="#result eq null") |
Spring 提供了多个用来定义缓存规则的SpEL表达式
表达式 | 描述 |
---|---|
#root.args | 传递给缓存方法的参数,形式为数组 |
#root.caches | 该方法执行时对应的缓存,形式为数组 |
#root.target | 目标对象 |
#root.targetClass | 目标对象的类,是 #root.target.class 的简写形式 |
#root.method | 缓存方法 |
#root.methodName | 缓存方法的名称,是#root.method.name 的简写形式 |
#result | 方法调用的返回值 |
Argument | 任意的方法参数名(例如 #username)或参数索引(例如 #a0 或 #p0) |
3、@CacheEvict:配置在方法或类上,作用:清除指定的一个或多个缓存。
参数如下:
参数名称 | 类型 | 解析 | 示例 |
---|---|---|---|
value | String[] | 要清除的缓存名称 | @CachEvict(value=“user:loginUser”) @CachEvict(value={“user:loginUser”,“myCache”} |
key | String | SpEL表达式,缓存的key,可以为空 | @CachEvict(value=“user:loginUser”, key="#username") |
condition | String | SpEL表达式,缓存的条件,可以为空,只有为true才清除缓存 | @CachEvict(value=“myCache”, condition="#username.length()>2") |
allEntries | Boolean | 默认为false,如果为true,会清除指定的所有缓存 | @Cacheable(value=“user:loginUser”, allEntries=true) |
beforeInvocation | Boolean | 默认为false,如果为true,在方法调用之前清除缓存,如果为false,在方法调用之后清除缓存 | @Cacheable(value=“myCache”, beforeInvocation=true) |
九、在SpringBoot中使用Redis
1. 引入redis的启动器依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. application.yml
spring:
# redis的配置
redis:
host: localhost
database: 1
port: 6379
3. 配置类
@Configuration
/**
* @EnableCaching
* 作用:开启注解式缓存
*/
@EnableCaching
public class RedisConfig {
/**
* 1.配置redisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
/**
* 2.json序列化
*/
@Bean
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
return serializer;
}
/**
* 配置缓存管理器
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认过期时间,也是使用Duration设置
config = config.entryTtl(Duration.ofMinutes(30))
// 设置 key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
// 不缓存空值
.disableCachingNullValues();
// 设置一个初始化的缓存空间set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add("default:cache");
cacheNames.add("user:login:cache");
// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("default:cache", config);
configMap.put("user:login:cache", config.entryTtl(Duration.ofMinutes(120)));
// 使用自定义的缓存配置初始化一个cacheManager
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
// 一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}
@Bean
public RedisUtil redisUtil(){
return new RedisUtil();
}
}
4. 使用与测试
@Service
public class CacheService {
@Cacheable(value = "user:login:cache", key = "#username", unless = "#result eq null")
public User cacheUser(String username) {
System.out.println("cacheUser..." + username);
if(StringUtils.isEmpty(username)) {
return null;
}
User user = new User(1, username, "123", "13012312312");
return user;
}
}
.defaultCacheConfig();
// 设置缓存的默认过期时间,也是使用Duration设置
config = config.entryTtl(Duration.ofMinutes(30))
// 设置 key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()))
// 不缓存空值
.disableCachingNullValues();
// 设置一个初始化的缓存空间set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add("default:cache");
cacheNames.add("user:login:cache");
// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("default:cache", config);
configMap.put("user:login:cache", config.entryTtl(Duration.ofMinutes(120)));
// 使用自定义的缓存配置初始化一个cacheManager
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
// 一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}
@Bean
public RedisUtil redisUtil(){
return new RedisUtil();
}
}
### 4. 使用与测试
```java
@Service
public class CacheService {
@Cacheable(value = "user:login:cache", key = "#username", unless = "#result eq null")
public User cacheUser(String username) {
System.out.println("cacheUser..." + username);
if(StringUtils.isEmpty(username)) {
return null;
}
User user = new User(1, username, "123", "13012312312");
return user;
}
}
更多推荐
所有评论(0)