1,spring-data-redis官网
1)特点
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)
- 提供了RedisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring独享的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
2,RedisTemplate
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。
1)常用API
api | 说明 |
---|---|
redisTemplate.opsForValue(); | 操作字符串 |
redisTemplate.opsForHash(); | 操作hash |
redisTemplate.opsForList(); | 操作list |
redisTemplate.opsForSet(); | 操作set |
redisTemplate.opsForZSet(); | 操作有序set |
redisTemplate.expire(key, 60 * 10000 * 30, TimeUnit.MILLISECONDS); | 设置过期时间 |
1>API:String
redisTemplate.opsForValue().set("name","tom")
说明 | api | 备注 |
---|---|---|
添加单值 | .set(key,value) |
|
获取单值 | .get(key) |
|
添加单值并返回这个值是否已经存在 | .setIfAbsent(key, value) |
|
添加单值并返回旧值 | .getAndSet(key, value) |
|
批量添加 |
Map<String,String> maps; .multiSet(maps)
|
|
批量获取 |
List<String> keys; .multiGet(keys)
|
|
数值型+1 | .increment(key,1) |
|
设置过期时间 | set(key, value, timeout, TimeUnit.SECONDS) |
过期返回null |
字符串追加 | .append(key,"Hello"); |
2>API:List数据
template.opsForList().range("list",0,-1)
说明 | api | 备注 |
---|---|---|
单个插入 | Long leftPush(key, value); | 返回操作后的列表的长度 |
批量插入 | Long leftPushAll(K key, V… values); | 返回操作后的列表的长度; values可以是 String[] 、List<Object>
|
查看 | .range(key,0,-1) |
从左往右:0,1,2; 从右往左:-1,-2,-3; 可做分页 |
弹出最左边的元素 | .leftPop("list") |
弹出之后该值在列表中将不复存在 |
修改 | set(key, index, value) |
|
key存在则插入 | Long rightPushIfPresent(K key, V value); |
返回操作后的列表的长度 |
求subList | .trim(key,1,-1) |
|
移除元素 | Long remove(key, long count, Object value); | count> 0:删除从左到右共count个等于value的元素。 count <0:删除等于从右到左共count个等于value的元素。 count = 0:删除等于value的所有元素。 |
求长度 | .size(key) |
3>API:Hash操作
一个key1对应一个Map,map中每个value中@class
后面对应的值为类信息。
template.opsForHash().put("redisHash","name","tom");
说明 | api | 备注 |
---|---|---|
单插入 | .put(redisKey,hashKey, value) |
|
批量插入 |
Map<String,Object> map .putAll(key, map)
|
|
查单数据 | .get(redisKey,hashKey) |
|
查所有数据 | .entries(redisHash) |
|
查key是否存在 | Boolean hasKey(redisKey, Object hashKey); |
|
批量获取Hash值 | List multiGet(redisKey, List<Object> kes); |
|
获取key集合 | Set keys(redisKey) |
|
批量删除 | Long delete(redisKey, Object… hashKeys) |
|
数值型value + 5 | increment(redisKey, hashKey, 5) | 返回操作后的value值 |
hashkey不存在时设置value | Boolean putIfAbsent(redisKey,hashKey, value) |
存在返回true,不存在返回true |
遍历:
Cursor<Map.Entry<Object, Object>> curosr = template.opsForHash().scan("redisHash", ScanOptions.ScanOptions.NONE);
while(curosr.hasNext()){
Map.Entry<Object, Object> entry = curosr.next();
System.out.println(entry.getKey()+":"+entry.getValue());
}
4>API:Set数据
template.opsForSet().add(k,v)
说明 | api | 备注 |
---|---|---|
添加 | Long add(key, V… values); |
values可以是:String[]
|
查看所有 | .members(key) | |
查询长度 | .size(key) | |
查询元素是否存在 | Boolean isMember(key, Object o); |
|
批量删除 | Long remove(key, Object… values); |
values可以是:String[]
|
随机移除 | V pop(K key); |
|
将元素value 从 sourcekey所在集合移动到 destKey所在集合 | Boolean move(sourcekey, V value, destKey) |
移动后sourcekey集合再没有value元素,destKey集合去重。 |
求两个集合的交集 | Set intersect(K key, K otherKey); |
|
求多个无序集合的交集 | Set intersect(K key, Collection otherKeys); |
|
求多个无序集合的并集 | Set union(K key, Collection otherKeys); |
遍历:
Cursor<Object> curosr = template.opsForSet().scan("setTest", ScanOptions.NONE);
while(curosr.hasNext()){
System.out.println(curosr.next());
}
5>API:ZSet集合
有序的Set集合,排序依据是Score。
template.opsForZSet().add("zset1","zset-1",1.0)
说明 | api | 备注 |
---|---|---|
添加单个元素 | Boolean add(k, v, double score) |
返回元素是否已存在 |
批量添加元素 | Long add(k, Set<TypedTuple> tuples) |
举例:见下文1. |
批量删除 | Long remove(K key, Object… values); |
|
排序按分数值asc,返回成员o的排名 | Long rank(key, Object o); |
排名从0开始 |
排序按分数值desc,返回成员o的排名 | Long reverseRank(key, Object o); |
排名从0开始 |
按区间查询,按分数值asc | Set range(key, 0, -1); |
|
增加元素的score值,并返回增加后的值 | Double incrementScore(K key, V value, double delta); |
- 批量添加元素
ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<Object>("zset-5",9.6);
ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<Object>("zset-6",9.9);
Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<ZSetOperations.TypedTuple<Object>>();
tuples.add(objectTypedTuple1);
tuples.add(objectTypedTuple2);
System.out.println(template.opsForZSet().add("zset1",tuples));
System.out.println(template.opsForZSet().range("zset1",0,-1));
- 遍历
Cursor<ZSetOperations.TypedTuple<Object>> cursor = template.opsForZSet().scan("zzset1", ScanOptions.NONE);
while (cursor.hasNext()){
ZSetOperations.TypedTuple<Object> item = cursor.next();
System.out.println(item.getValue() + ":" + item.getScore());
}
2)使用
1>依赖
<!-- Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2>配置文件
spring:
redis:
host: 127.0.0.1 # Redis服务器地址
port: 6379 # Redis服务器连接端口
timeout:0 # 连接超时时间(毫秒)
# database: 0 # Redis数据库索引(默认为0)
# password: # Redis服务器连接密码(默认为空)
lettuce: # 使用的是lettuce连接池
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
max-wait: 100 # 连接池最大阻塞等待时间(使用负值表示没有限制)
1>序列化配置
RedisTemplate默认采用JDK的序列化工具,序列化为字节形式,在redis中可读性很差。
修改默认的序列化方式为jackson:
@Configuration
public class RedisConfig {
@Bean //RedisConnectionFactory不需要我们创建Spring会帮助我们创建
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 1.创建RedisTemplate对象
RedisTemplate<String,Object> template = new RedisTemplate<>();
// 2.设置连接工厂
template.setConnectionFactory(connectionFactory);
// 3.创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 4.设置Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 5.设置Value的序列化 jsonRedisSerializer使我们第三步new出来的
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 6.返回
return template;
}
}
但是json序列号可能导致一些其他的问题:JSON序列化器会将类的class类型写入到JSON结果中并存入Redis,会带来额外的内存开销。
为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key哈value,当要存储Java对象时,手动完成对象的序列化和反序列化。
4>java实现
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* 批量删除对应的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量删除key
*
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
}
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
public String get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
if (result == null) {
return null;
}
return result.toString();
}
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public boolean hmset(String key, Map<String, String> value) {
boolean result = false;
try {
redisTemplate.opsForHash().putAll(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public Map<String, String> hmget(String key) {
Map<String, String> result = null;
try {
result = redisTemplate.opsForHash().entries(key);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
3)StringRedisTemplate
key和value的序列化方式默认就是String方式,省去了我们自定义RedisTemplate的过程。
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testStringTemplate() throws JsonProcessingException {
// 准备对象
User user = new User("abc", 18);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入一条数据到Redis
stringRedisTemplate.opsForValue().set("user:200",json);
// 读取数据
String s = stringRedisTemplate.opsForValue().get("user:200");
// 反序列化
User user1 = mapper.readValue(s,User.class);
System.out.println(user1);
}
3,Redis数据序列化
4,Repository操作
类似jpa操作,只要Redis服务器版本在2.8.0以上,不用事务,就可以使用Repository做各种操作。
注意:Repository和jpa的Repository是同一个,意味着jpa支持的api在redis操作中通用。
1)注解
注解 | 说明 | 属性 | 对比jpa |
---|---|---|---|
@RedisHash | 用来定义实体。 |
value :定义了不同类型对象存储时使用的前缀,也叫做键空间,默认是全限定类名;timeToLive 定义缓存的秒数; |
类似@Entity |
@Id | 定义对象的标识符 | 类似@Id | |
@Indexed | 定义二级索引,加在属性上可以将该属性定义为查询用的索引 | ||
@Reference | 缓存对象引用,一般引用的对象也会被展开存储在当前对象中,添加了该注解后会直接存储该对象在Redis中的引用 |
2)使用
- 不需要添加依赖。
spring-boot自动添加了@EnableRedisRepositories注解。 - 实体
@RedisHash(value="menu",timeToLive=60)
public class RedisMenuItem implements Serializable{
@Id
privste Long id;
@Indexed
private String name;
private Size size;
private Money price;
}
- 定义Repository接口
public interface RedisMenuRepository extends CrudRepository<RedisMenuItem, Long>{
List<RedisMenuItem> findByName(String name);
}
- 查询
List<MenuItem> itemList = menuRepository.findAll();
menuRepository.save(menuItem);
5,Spring Cache
Spring 3.1 引入了对 Cache 的支持,使用使用 JCache(JSR-107)注解简化开发。
注意:可支持幂等操作的接口才可以使用缓存注解,因为缓存和参数无关,即:不管什么参数,返回值一样。
1)org.springframework.cache.Cache接口
- 包含了缓存的各种操作集合;
- 提供了各种xxxCache 的实现,比如:RedisCache
2)org.springframework.cache.CacheManager接口
- 定义了创建、配置、获取、管理和控制多个唯一命名的 Cache。这些 Cache 存在于 CacheManager 的上下文中。
- 提供了各种xxxCacheManager 的实现,比如RedisCacheManager。
3)相关注解
1>@EnableCaching
- 开启基于注解的缓存;
- 作用在缓存配置类上或者SpringBoot 的主启动类上;
2>@Cacheable
缓存注解。
使用注意:
- 基于AOP去实现的,所以必须通过IOC对象去调用。
- 要缓存的 Java 对象必须实现 Serializable 接口。
@Cacheable(
cacheNames = "usersBySpEL",
//key通过变量拼接
key="#root.methodName + '[' + #id + ']'",
//id大于1才缓存。可缺省
condition = "#id > 1",
//当id大于10时,条件为true,方法返回值不会被缓存。可缺省
unless = "#id > 10")
public User getUserBySpEL(Integer id) {
}
@Cacheable(value = {"menuById"}, key = "'id-' + #menu.id")
public Menu findById(Menu menu) {
return menu;
}
常用属性 | 说明 | 备注 | 代码示例 |
---|---|---|---|
cacheNames/value | 缓存名称,用来划分不同的缓存区,避免相同key值互相影响。 | 可以是单值、数组; 在redis中相当于key的一级目录,支持 : 拼接多层目录 |
cacheNames = "users" cacheNames = {"users","account"}
|
key | 缓存数据时使用的 key,默认是方法参数。 可以使用 spEL 表达式来编写 |
||
keyGenerator | key 的生成器,统一管理key。 | key 和 keyGenerator 二选一使用,同时使用会导致异常。 | keyGenerator = "myKeyGenerator" <相关文章
|