抢占先机
VMware 提供培训和认证,助您快速进步。
了解更多Spring Data 的 Evans 版本已经发布一段时间了,现在是时候向大家介绍随之而来的最新、最出色的功能了。
有很多内容要讲,因为主要的增强功能都集中在 commons 模块中。这些更改已经进入了一些存储模块,并且会随着时间的推移悄悄地进入其他模块。所有这些增强功能至少已在 Spring Data JPA 中可用。话不多说,让我们直接开始吧。
Java 8 已经推出一段时间了,之前的 Spring Data 版本列车已经为其添加了一些基础支持。通过 Evans 版本列车,我们显著扩展了对其的支持。
从 Dijkstra 版本开始,Java 8 的 Optional
就已支持作为返回值,使您无需在代码中到处进行 null
检查。当在仓库中使用它作为返回类型时,我们会为您简单地包装和解包值。
从 Evans 版本开始,默认方法可以在仓库接口中使用,例如将传入方法的参数部分转发到其他查询方法。
interface PersonRepository extends Repository<Person, Long> {
Optional<Customer> findByLastname(String lastname);
default Optional<Customer> findByLastname(Customer customer) {
return findByLastname(customer == null ? null : customer.getLastname());
}
}
到目前为止,配置应用程序以使用不同的 Spring Data 模块并非没有问题。例如,您可能希望结合使用 JPA 和 MongoDB,其中 Customer
是一个 JPA 实体,而 Order
是一个 MongoDB 文档,两者都通过相应的仓库接口进行持久化。
@Entity
class Customer {
@Id @GeneratedValue Long id;
String firstname, lastname;
// ...
}
@Document
class Order {
@Id String id;
Long customerId;
Date orderDate;
// ...
}
interface CustomerRepository extends CrudRepository<Customer, Long> {}
interface OrderRepository extends CrudRepository<Order, String> {}
在 Spring Data Evans 版本之前,您必须手动配置 MongoDB 和 JPA 的仓库设置,以相互排除与给定存储不相关的接口。用户通常为此使用单独的包。
现在,仓库设置会检测到类路径上存在多个 Spring Data 模块,并自动限制仓库扫描,并检查给定仓库使用的领域类型是否存在特定于存储的注解,如 @Entity
和 @Document
,以确定它们属于哪个具体实现。例如,如果 Spring Data MongoDB 模块没有找到 @Document
注解,它就会忽略(意外检测到的)CustomerRepository
。
动态限制结果数并不是新概念,因为 Spring Data 自创建以来就提供了 Pagable
抽象,我敢打赌几乎每个 Spring Data 用户都熟悉类似这样的用法
List<Person> findByLastname(String lastname, Pageable page)
这种方法声明提供了相当大的灵活性:客户端定义要访问的元素的页码、大小和排序顺序。如果这些值是动态变化的(例如逐页遍历结果集时),这会非常有用。
但是,如果您总是只对前 10 个结果感兴趣,并且总是希望按姓氏对它们进行排序呢?这可以通过静态定义一个 PageRequest
并在每次方法调用时重用它来实现。然而,这仍然需要客户端传入特殊的 PageRequest
。
从 Spring Data Evans 版本开始,我们现在为您提供一种便捷的方式,通过使用关键字 top
和 first
后跟可选的正数值(默认为 1),明确地将结果集限制为一定数量的元素。
List<Person> findTop10ByLastnameOrderByFirstnameDesc(String lastname);
Evans RC1 版本引入了对 MongoDB 2.6 的基础文本索引支持。使用 @TextIndexed
可以标记您希望启用文本搜索的属性,以便我们为您创建索引。请注意,在引用复杂类型的属性上放置 @TextIndexed
将索引该类型的所有属性。由于评分是全文搜索的基本组成部分,@TextScore
注解将确保任何全文查询都返回文档的得分,从而允许您按相关性对它们进行排序。
@Document
class BlogPost {
@Id String id;
@TextIndexed(weight = 3) String title;
@TextIndexed(weight = 2) String content;
@TextIndexed List<String> categories;
@TextScore Float score;
}
在此基础上,我们扩展了仓库支持,使其接受一个 TextCriteria
实例,该实例将定义要执行的文本搜索的详细选项:要搜索的词条、语言选项等。
interface BlogPostRepository extends CrudRepository<BlogPost, String> {
Page<BlogPost> findBy(TextCriteria criteria, Pageable page);
List<BlogPost> findAllByOrderByScoreDesc(TextCriteria criteria);
}
第一个查询方法相当直观。它执行给定的 TextCriteria
并对结果进行分页。第二个查询方法定义将给定的 TextCriteria
与从方法名派生的标准条件定义相结合。这表明您可以轻松地将文本搜索与标准查询自由组合。
我们添加了 @Meta
注解,允许您定义查询的输出和行为。例如,通过设置 maxExecutionTime
可以定义查询可能花费的最大持续时间(以毫秒为单位)。任何超过限制的执行都会导致错误。您还可以通过设置 maxScanDocuments
建议 MongoDB 只扫描最多数量的文档,并在达到限制时返回已找到的内容;而 comment
则允许您定义文本,以便在为您的 MongoDB 实例启用了性能分析的情况下,在 system.profile
集合中搜索。
@Meta(maxExcecutionTime = 100, comment = "onlyLimitedTime")
List<Customer> findByFirstname(String firstname);
Redis 2.8 引入了称为 Sentinels 的高可用性支持。Spring Data Evans 的 Redis 模块添加了对轻松配置连接到 Sentinel 设置的支持,这样您的客户端在 Redis 集群中主节点重新选举的情况下也能继续工作。
RedisSentinelConfiguration
定义了 Sentinel 的位置,以便 ConnectionFactory
可以相应地设置连接池。对于 Jedis 而言,它将创建一个 JedisSentinelPool
用于自动故障转移。这意味着如果您的主节点发生故障,一旦 Sentinel 协商出新的主节点,您将立即获得到新主节点的连接,无需任何进一步的干预。
@Configuration
class RedisSentinelApplicationConfig {
@Bean
RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(sentinelConfig());
}
@Bean
RedisSentinelConfiguration sentinelConfig() {
return new RedisSentinelConfiguration().master("mymaster")
.sentinel("localhost", 26379)
.sentinel("localhost", 26380)
.sentinel("localhost", 26381);
}
}
即将发布的 Spring Boot 1.2 将进一步深化此功能,如果存在 RedisSentinelConfiguration
,它将自动识别并相应地初始化 RedisConnectionFactory
。
尽管 Solr Schema API 尚未最终完成,但我们已尽力支持其尽可能多的部分。通过 Evans 版本,您现在可以动态地将缺失的字段添加到现有的(托管)schema 中。为此,我们会读取现有的字段定义,并将其与从领域类型的属性派生出的定义进行比较。为了做到这一点,我们对 @Indexed
注解进行了一些扩展。现在它允许对要创建的字段进行一些微调,可以明确定义诸如 indexed
、stored
和 solrType
等值。
@Configuration
@EnableSolrRepositories(schemaCreationSupport = true)
class SolrConfiguration {
@Bean
SolrServer solrServer() {
return new HttpSolrServer("http://localhost:8983/solr");
}
}
@SolrDocument(solrCoreName = "collection1")
class ManagedProduct {
@Id String id;
@Indexed(type = "text_general") String name;
@Indexed(name = "cat") List<String> category;
}
一如既往,我们期待听到您的反馈!请通过 Twitter、Stackoverflow 或 JIRA 联系我们,提出新功能请求、改进建议或报告错误。