8. Redis存储库

使用Redis存储库允许在Redis哈希中无缝地转换和存储域对象,应用自定义映射策略并利用二级索引。

Redis存储库至少需要Redis Server 2.8.0版。

8.1。用法

要访问存储在Redis中的域实体,您可以利用存储库支持,从而轻松实现这些实现。

示例5.示例人员实体
@RedisHash("persons")
public class Person {@Id String id;String firstname;String lastname;Address address;
}

我们在这里有一个非常简单的域对象。请注意,它有一个名为idannotated 的属性org.springframework.data.annotation.Id和一个@RedisHash注释类型。这两个负责创建用于保存散列的实际密钥。

注释的属性@Id以及命名id的属性被视为标识符属性。那些带有注释的人比其他人更受青睐。

现在实际上有一个负责存储和检索的组件,我们需要定义一个存储库接口。

示例6. Persist个体实体的基本知识库接口
public interface PersonRepository extends CrudRepository<Person, String> {}

随着我们的资源库的扩展,CrudRepository它提供了基本的CRUD和查找操作。我们需要将两者粘合在一起的是Spring配置。

示例7.用于Redis存储库的JavaConfig
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {@Beanpublic RedisConnectionFactory connectionFactory() {return new JedisConnectionFactory();}@Beanpublic RedisTemplate<?, ?> redisTemplate() {RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();return template;}
}

鉴于上面的设置,我们可以继续并注入PersonRepository我们的组件。

示例8.访问人员实体
@Autowired PersonRepository repo;public void basicCrudOperations() {Person rand = new Person("rand", "al'thor");rand.setAddress(new Address("emond's field", "andor"));repo.save(rand);                                         repo.findOne(rand.getId());                              repo.count();                                            repo.delete(rand);
}

如果当前值是null或重新使用已经设置的id值,则生成一个新的id Person,并keyspace:id在此情况下将具有模式的键存储在Redis Hash中的类型属性,例如。persons:5d67b7e1-8640-4475-beeb-c666fab4c0e5
使用提供的ID来检索存储在的对象keyspace:id
统计密钥空间内提供实体总数人的定义@RedisHashPerson
从Redis中删除给定对象的键。

8.2。对象到哈希映射

Redis Repository支持持久化Hashes中的对象。这需要一个由a完成的对象哈希转换RedisConverter。默认实现Converter用于将属性值映射到Redis本地和从Redis本地映射byte[]

鉴于Person前面几节中的类型,默认映射如下所示:

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address.city = emond's field
address.country = andor

_class属性包含在根级别以及任何嵌套的接口或抽象类型中。
简单的属性值由路径映射。
复杂类型的属性由它们的点路径映射。

表7.默认映射规则
类型 样品 映射值

简单类型
(例如字符串)

String firstname =“rand”;

firstname =“rand”

复杂类型
(例如地址)

address address = new Address(“emond's field”);

address.city =“emond的字段”

简单类型列表

列表<String> nicknames = asList(“龙重生”,“lews therin”);

昵称。[0] =“龙重生”,
绰号。[1] =“lews therin”

简单类型的地图

Map <String,String> atts = asMap({“eye-color”,“gray”},{“...

atts。[eye-color] =“gray”,
atts。[hair-color] =“...

复杂类型列表

List <Address> addresses = asList(new Address(“em ...

地址。[0] .city =“emond's field”,
地址。[1] .city =“...

复杂类型的地图

Map <String,Address> addresses = asMap({“home”,new Address(“em ...

地址。[home] .city =“emond's field”,
地址。[work] .city =“...

由于平面表示结构Map键需要是简单的类型,如Strings或Numbers。

映射行为可以通过根据注册定制ConverterRedisCustomConversions。这些转换器可以处理从单个转换到另一个byte[]Map<String,byte[]>而第一个转换器适用于例如。将一种复杂类型转换为例如。二进制JSON表示仍然使用默认映射哈希结构。第二个选项提供了对最终散列的完全控制。将对象写入Redis哈希将从哈希中删除内容并重新创建整个哈希,因此未映射的数据将丢失。

例9.采样字节[]转换器
@WritingConverter
public class AddressToBytesConverter implements Converter<Address, byte[]> {private final Jackson2JsonRedisSerializer<Address> serializer;public AddressToBytesConverter() {serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);serializer.setObjectMapper(new ObjectMapper());}@Overridepublic byte[] convert(Address value) {return serializer.serialize(value);}
}@ReadingConverter
public class BytesToAddressConverter implements Converter<byte[], Address> {private final Jackson2JsonRedisSerializer<Address> serializer;public BytesToAddressConverter() {serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);serializer.setObjectMapper(new ObjectMapper());}@Overridepublic Address convert(byte[] value) {return serializer.deserialize(value);}
}

使用上述字节[] Converter产生例如。

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address = { city : "emond's field", country : "andor" }

示例10.示例图<String,byte []>转换器
@WritingConverter
public class AddressToMapConverter implements Converter<Address, Map<String,byte[]>> {@Overridepublic Map<String,byte[]> convert(Address source) {return singletonMap("ciudad", source.getCity().getBytes());}
}@ReadingConverter
public class MapToAddressConverter implements Converter<Address, Map<String, byte[]>> {@Overridepublic Address convert(Map<String,byte[]> source) {return new Address(new String(source.get("ciudad")));}
}

使用上面的Map Converter生成例如。

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
ciudad = "emond's field"

自定义转换对索引分辨率没有影响。即使对于自定义转换类型,二级索引仍将被创建。

8.3。Keyspaces

密钥空间定义用于为Redis哈希创建实际密钥的前缀。默认情况下,前缀设置为getClass().getName()。此默认值可以通过@RedisHash聚合根级别或通过设置编程配置来更改。但是,带注释的密钥空间将取代任何其他配置。

示例11.通过@EnableRedisRepositories进行Keyspace设置
@Configuration
@EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class ApplicationConfig {//... RedisConnectionFactory and RedisTemplate Bean definitions omittedpublic static class MyKeyspaceConfiguration extends KeyspaceConfiguration {@Overrideprotected Iterable<KeyspaceSettings> initialConfiguration() {return Collections.singleton(new KeyspaceSettings(Person.class, "persons"));}}
}

示例12.编程式Keyspace设置
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {//... RedisConnectionFactory and RedisTemplate Bean definitions omitted@Beanpublic RedisMappingContext keyValueMappingContext() {return new RedisMappingContext(new MappingConfiguration(new MyKeyspaceConfiguration(), new IndexConfiguration()));}public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {@Overrideprotected Iterable<KeyspaceSettings> initialConfiguration() {return Collections.singleton(new KeyspaceSettings(Person.class, "persons"));}}
}

8.4。二级索引

二级索引用于启用基于本机Redis结构的查找操作。值在每次保存时写入相应的索引,并在删除或过期时删除。

8.4.1。简单的财产指数

给定示例Person实体,我们可以通过注释属性为firstname创建一个索引@Indexed

示例13.注释驱动索引
@RedisHash("persons")
public class Person {@Id String id;@Indexed String firstname;String lastname;Address address;
}

索引是为实际属性值构建的。挽救两个人,例如。“rand”和“aviendha”导致设置如下所示的索引。

SADD persons:firstname:rand e2c7dcee-b8cd-4424-883e-736ce564363e
SADD persons:firstname:aviendha a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56

在嵌套元素上也可以有索引。假设Address有一个城市属性,注明@Indexed。在这种情况下,一旦person.address.city不是null,我们就为每个城市设置了套数。

SADD persons:address.city:tear e2c7dcee-b8cd-4424-883e-736ce564363e

此外,编程设置允许在地图键和列表属性上定义索引。

@RedisHash("persons")
public class Person {// ... other properties omittedMap<String,String> attributes;      Map<String Person> relatives;       List<Address> addresses;
}

SADD persons:attributes.map-key:map-value e2c7dcee-b8cd-4424-883e-736ce564363e
SADD persons:relatives.map-key.firstname:tam e2c7dcee-b8cd-4424-883e-736ce564363e
SADD persons:addresses.city:tear e2c7dcee-b8cd-4424-883e-736ce564363e

索引将不会在引用上解析。

与密钥空间相同,可以配置索引而不需要注释实际的域类型。

示例14.通过@EnableRedisRepositories设置索引
@Configuration
@EnableRedisRepositories(indexConfiguration = MyIndexConfiguration.class)
public class ApplicationConfig {//... RedisConnectionFactory and RedisTemplate Bean definitions omittedpublic static class MyIndexConfiguration extends IndexConfiguration {@Overrideprotected Iterable<IndexDefinition> initialConfiguration() {return Collections.singleton(new SimpleIndexDefinition("persons", "firstname"));}}
}

例子15.编程索引设置
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {//... RedisConnectionFactory and RedisTemplate Bean definitions omitted@Beanpublic RedisMappingContext keyValueMappingContext() {return new RedisMappingContext(new MappingConfiguration(new KeyspaceConfiguration(), new MyIndexConfiguration()));}public static class MyIndexConfiguration extends IndexConfiguration {@Overrideprotected Iterable<IndexDefinition> initialConfiguration() {return Collections.singleton(new SimpleIndexDefinition("persons", "firstname"));}}
}

8.4.2。地理空间索引

假设该Address类型包含保存特定地址的地理坐标的location类型属性Point。通过@GeoIndexed使用Redis GEO命令添加这些值来注释属性。

@RedisHash("persons")
public class Person {Address address;// ... other properties omitted
}public class Address {@GeoIndexed Point location;// ... other properties omitted
}public interface PersonRepository extends CrudRepository<Person, String> {List<Person> findByAddressLocationNear(Point point, Distance distance);     List<Person> findByAddressLocationWithin(Circle circle);
}Person rand = new Person("rand", "al'thor");
rand.setAddress(new Address(new Point(13.361389D, 38.115556D)));repository.save(rand);                                                        repository.findByAddressLocationNear(new Point(15D, 37D), new Distance(200)); 

使用点和距离查询嵌套属性的方法声明。
使用Circle在内搜索嵌套属性的查询方法声明。
GEOADD persons:address:location 13.361389 38.115556 e2c7dcee-b8cd-4424-883e-736ce564363e
GEORADIUS persons:address:location 15.0 37.0 200.0 km

在上面的例子中,GEOADD使用对象id作为成员的名字来存储lon / lat值。查找器方法允许查询这些值的用法CirclePoint, Distance组合。

这是不是可以组合nearwithin与其他标准。

8.5。生存时间

存储在Redis中的对象只能在一段时间内有效。这对于在Redis中保留短暂的对象尤其有用,而不必在到达其寿命时手动删除它们。以秒为单位的到期时间可以通过@RedisHash(timeToLive=…​)以及通过设置KeyspaceSettings(请参阅密钥空间)。

可以通过@TimeToLive在数字属性或方法上使用注释来设置更灵活的到期时间。但是,不适用于@TimeToLive同一个类中的方法和属性。

例子16.过期
public class TimeToLiveOnProperty {@Idprivate String id;@TimeToLiveprivate Long expiration;
}public class TimeToLiveOnMethod {@Idprivate String id;@TimeToLivepublic long getTimeToLive() {return new Random().nextLong();}
}

显式注释属性将从Redis @TimeToLive回读实际值TTLPTTL值。-1表示该对象没有过期关联。

存储库实现确保通过通过订阅Redis密钥空间通知RedisMessageListenerContainer

当到期被设置为正值时,EXPIRE执行相应的命令。除了保留原始文件外,幻影副本在Redis中保留并设置为在原始文件后5分钟到期。这样做是为了使存储库支持能够在密钥过期时RedisKeyExpiredEvent通过泉发布持有过期值,ApplicationEventPublisher即使原始值已经消失。所有连接的应用程序将使用Spring Data Redis存储库接收到期事件。

默认情况下,初始化应用程序时,密钥到期监听器被禁用。启动模式可以在应用程序中调整@EnableRedisRepositoriesRedisKeyValueAdapter启动监听程序,也可以在首次插入具有TTL的实体时启动。查看EnableKeyspaceEvents可能的值。

RedisKeyExpiredEvent会保存实际过期的域对象的副本以及密钥。

延迟或禁用到期事件侦听器启动会影响RedisKeyExpiredEvent发布。禁用的事件侦听器不会发布到期事件。由于延迟侦听器初始化,延迟启动可能导致事件丢失。
密钥空间通知消息侦听器将改变notify-keyspace-eventsRedis中的设置(如果这些设置尚未设置)。现有的设置不会被覆盖,所以留给用户的时候不要将它们留空。请注意,CONFIG在AWS ElastiCache中禁用了此功能,并且启用了侦听器导致的错误。
Redis Pub / Sub消息不是持久的。如果在应用程序关闭期间某个键过期,则不会处理到期事件,这可能会导致二级索引包含对已过期对象的静态引用。

8.6。坚持参考

使用标记属性@Reference允许存储简单的键引用,而不是将值复制到散列本身。在从Redis加载时,引用会自动解析并映射回对象。

示例17.示例属性参考
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
mother = persons:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56      

引用存储keyspace:id引用对象的整个键()。

引用对象在保存引用对象时不会保留更改。请确保分开保存对引用对象的更改,因为只有引用将被存储。在引用类型的属性上设置的索引不会被解析。

8.7。坚持部分更新

在某些情况下,不需要加载和重写整个实体,只需在其中设置一个新值即可。上次活动时间的会话时间戳可能是您只想更改一个属性的场景。 PartialUpdate允许在现有对象上定义setdelete操作,同时考虑更新实体本身的潜在到期时间以及索引结构。

示例18.示例部分更新
PartialUpdate<Person> update = new PartialUpdate<Person>("e2c7dcee", Person.class).set("firstname", "mat")                                                           .set("address.city", "emond's field")                                              .del("age");                                                                       template.update(update);update = new PartialUpdate<Person>("e2c7dcee", Person.class).set("address", new Address("caemlyn", "andor"))                                   .set("attributes", singletonMap("eye-color", "grey"));                             template.update(update);update = new PartialUpdate<Person>("e2c7dcee", Person.class).refreshTtl(true);                                                                 .set("expiration", 1000);template.update(update);

将简单属性firstname设置为mat。
将简单属性address.city设置为emond的字段,而不必传入整个对象。这在注册自定义转换时不起作用。
删除楼龄。
设置复杂的地址。
设置一个地图/值集合会删除先前存在的地图/集合,并用给定值替换这些值。
更改生存时间时自动更新服务器到期时间。

更新复杂对象以及映射/集合结构需要与Redis进一步交互以确定现有值,这意味着可能会发现重写整个实体可能会更快。

8.8。查询和查询方法

查询方法允许从方法名称自动派生简单的查找器查询。

示例19.样本库搜索器方法
public interface PersonRepository extends CrudRepository<Person, String> {List<Person> findByFirstname(String firstname);
}

请确保在查找器方法中使用的属性设置为索引。
Redis存储库的查询方法仅支持查询具有分页的实体和实体集合。

使用派生查询方法可能并不总是足以对要执行的查询建模。RedisCallback可以更好地控制索引结构的实际匹配,甚至可以自定义添加索引结构。它只需提供一个RedisCallback返回一个或一Iterable组id值的方法。

示例20.使用RedisCallback的示例查找程序
String user = //...List<RedisSession> sessionsByUser = template.find(new RedisCallback<Set<byte[]>>() {public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {return connection.sMembers("sessions:securityContext.authentication.principal.username:" + user);}}, RedisSession.class);

以下概述了Redis支持的关键字以及包含该关键字的方法。

表8.方法名称内的支持的关键字
关键词 样品 Redis片段

And

findByLastnameAndFirstname

SINTER …:firstname:rand …:lastname:al’thor

Or

findByLastnameOrFirstname

SUNION …:firstname:rand …:lastname:al’thor

Is,Equals

findByFirstnamefindByFirstnameIsfindByFirstnameEquals

SINTER …:firstname:rand

Top,First

findFirst10ByFirstnamefindTop5ByFirstname

 

8.9。运行在群集上的Redis存储库

在群集的Redis环境中使用Redis存储库支持很好。有关配置详细信息,请参阅Redis群集部分ConnectionFactory。仍然需要考虑一些因素,因为默认的密钥分配会将实体和二级索引分散到整个集群及其插槽中。

类型 插槽 节点

人数:e2c7dcee-b8cd-4424-883e-736ce564363e

ID为散列

15171

127.0.0.1:7381

人数:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56

ID为散列

7373

127.0.0.1:7380

人数:姓:兰特

指数

1700

127.0.0.1:7379

当所有涉及的密钥映射到相同的插槽时,一些命令只能在服务器端处理SINTER并且SUNION只能处理。否则,计算必须在客户端完成。因此将密钥空间固定到单个插槽可以立即使用Redis服务器计算。

类型 插槽 节点

{}人:e2c7dcee-b8cd-4424-883e-736ce564363e

ID为散列

2399

127.0.0.1:7379

{}人:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56

ID为散列

2399

127.0.0.1:7379

{}人:姓:兰特

指数

2399

127.0.0.1:7379

在使用Redis群集时,通过`@RedisHash(“{yourkeyspace}”)定义和固定密钥空间到特定的插槽。

8.10。CDI整合

存储库接口的实例通常由一个容器创建,当使用Spring Data时,Spring是最自然的选择。有复杂的支持来轻松​​设置Spring来创建bean实例。Spring Data Redis附带一个自定义CDI扩展,允许在CDI环境中使用存储库抽象。该扩展是JAR的一部分,因此您只需要将Spring Data Redis JAR放入类路径即可。

现在,您可以设置基础结构通过实施为一个CDI生产者RedisConnectionFactoryRedisOperations

class RedisOperationsProducer {@ProducesRedisConnectionFactory redisConnectionFactory() {JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(new RedisStandaloneConfiguration());jedisConnectionFactory.afterPropertiesSet();return jedisConnectionFactory;}void disposeRedisConnectionFactory(@Disposes RedisConnectionFactory redisConnectionFactory) throws Exception {if (redisConnectionFactory instanceof DisposableBean) {((DisposableBean) redisConnectionFactory).destroy();}}@Produces@ApplicationScopedRedisOperations<byte[], byte[]> redisOperationsProducer(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();template.setConnectionFactory(redisConnectionFactory);template.afterPropertiesSet();return template;}}

必要的设置可能因您运行的JavaEE环境而异。

Spring Data Redis CDI扩展将挑选所有可用作CDI bean的Repositories,并在容器请求存储库类型的bean时创建Spring Data repository的代理。因此,获取Spring Data存储库的一个实例就是声明一个@Injected属性的问题:

class RepositoryClient {@InjectPersonRepository repository;public void businessMethod() {List<Person> people = repository.findAll();}
}

Redis存储库需要RedisKeyValueAdapterRedisKeyValueTemplate实例。如果未找到提供的bean,则这些bean由Spring Data CDI扩展创建和管理。但是,您可以提供自己的豆子配置的特定属性RedisKeyValueAdapterRedisKeyValueTemplate

源码下载:https://github.com/daqiang123/basic

Spring Data Redis存储库相关推荐

  1. 定制Spring Data JPA存储库

    Spring Data是一个非常方便的库. 但是,由于该项目是一个相当新的项目,因此功能不佳. 默认情况下,Spring Data JPA将基于SimpleJpaRepository提供DAO的实现. ...

  2. 使用Spring Data REST将Spring Data JPA存储库导出为REST服务

    Spring Data模块提供了各种模块,以统一的方式处理各种类型的数据源,如RDBMS,NOSQL存储等. 在我以前的文章SpringMVC4 + Spring Data JPA +使用JavaCo ...

  3. 自定义Spring Data JPA存储库

    Spring Data是一个非常方便的库. 但是,由于该项目是一个相当新的项目,因此功能不佳. 默认情况下,Spring Data JPA将基于SimpleJpaRepository提供DAO的实现. ...

  4. 一文搞定 Spring Data Redis 详解及实战

    转载自  一文搞定 Spring Data Redis 详解及实战 SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问 ...

  5. Spring Data Redis 实践

    前言 Spring Data Redis是Spring Data大家族的一部分,提供了基于spring应用的简易配置与redis服务访问,它为存储与交互提供了低级(low-level)和高级的(hig ...

  6. Spring Data Redis 多源

    完整代码:Ciiiiing/springboot_multi_redis 最近需要在同一个项目中访问多个 redis 而 spring data redis 默认是只支持一个数据源的,那就需要我们自己 ...

  7. Spring Data Redis简单使用

    项目常见问题思考 在项目中发现:对于首页每天有大量的人访问,对数据库造成很大的访问压力,甚至是瘫痪.那如何解决呢?我们通常的做法有两种:一种是数据缓存.一种是网页静态化.我们今天讨论第一种解决方案.将 ...

  8. Spring data redis 异常

    2019独角兽企业重金招聘Python工程师标准>>> spring 集成 spring-data-redis 版本: spring低版本 + spring-data-redis 高 ...

  9. 使用Spring Data Redis操作Redis(集群版)

    说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点.并且会与一些低版本的Sp ...

最新文章

  1. .net中excel遇到的一些问题
  2. SylixOS普通定时器精度分析
  3. Java访问指示符 访问修饰符
  4. Java存储密码用字符数组
  5. 共赴CIO时代,永洪BI如何推动企业数字化转型与创新?
  6. return 返回值的问题
  7. java mllib 算法_朴素贝叶斯算法原理及Spark MLlib实例(Scala/Java/Python)
  8. codevs 5958 无
  9. sqlserver主键自增
  10. 从Qt的图片文件传输来看Qt 中UDP的收发消息writeDatagram和readDatagram
  11. 后台处理客户端控件上传的文件(input type=file)
  12. 电子相册系统(九)分页
  13. ubuntu 20.04 编译yocto 错误集锦
  14. 计算机图标制作教程,电脑主题ICO图标制作方法 详细教程你一学就会
  15. C++ typedef 用法详解
  16. 全网最全python教程,从零到精通(学python有它就够必收藏)
  17. 龙家贰少的MarkDown学习笔记
  18. 语音智能时代,我选择这个浪尖
  19. PHP:【商城后台管理系统】admin超级管理员后台操作界面部署{无限级菜单}
  20. kafka同步mysql数据报Possibly consider using a shorter maxLifetime value.

热门文章

  1. exists/not exists的使用方法
  2. 七天表达不了我对祖国的热爱
  3. NLP学习笔记-QA机器人(七)
  4. Mysql教程(九)---分组
  5. PPT 下载 | 每日一淘的高速增长与复购是怎样炼成的?
  6. 万万没想到,非计算机专业的我,也能进互联网大厂
  7. 用requests包爬取今日头条新闻标题
  8. HTML基础篇(标签和属性整--已剔除不被浏览器支持的部分)
  9. Windows 系统应急指令及服务
  10. python 函数参数注释_Python中函数添加注释 如何正确的为函数添加注释说明