Apache Solr 的 Spring Data (数据)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Apache Solr 的 Spring Data (数据)相关的知识,希望对你有一定的参考价值。
版本 4.3.15
Spring Data for Apache Solr项目通过使用Apache Solr搜索引擎将Spring的核心概念应用于解决方案的开发。我们提供了一个“模板”作为存储和查询文档的高级抽象。您可能会注意到与Spring Framework中的MongoDB支持相似之处。
项目元数据
- 版本控制:https://github.com/spring-projects/spring-data-solr
- 布塔克:https://jira.spring.io/browse/DATASOLR
- 发布库:https://repo.spring.io/libs-release
- 里程碑存储库:https://repo.spring.io/libs-milestone
- 快照存储库:https://repo.spring.io/libs-snapshot
要求
Spring Data Solr 需要 Java 8 运行时和Apache Solr8.0。我们建议使用最新的 8.0.x 版本。
以下 Maven 依赖项代码段显示了如何在项目中包含 Apache Solr:
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>$solr.version</version>
</dependency>
1. 新的和值得注意的
1.1. Apache Solr 2.1 春季数据中的新增功能
- 通过使用自动选择Solr内核。
SolrTemplate
- 声明向上兼容 Apache Solr 6(包括 6.3)。
- 支持组合和查询。
Facet
Highlight
- 允许将单值多值字段读取到非集合属性中。
- 使用本机 SolrJ 模式 API。
1.2. Apache Solr 2.0 春季数据中的新增功能
- 升级到 Apache Solr 5。
- 支持查询时。
RequestMethod
1.3. Apache Solr 1.5 春季数据中的新增功能
- 支持范围分面。
- 自动前缀和后缀映射键(请参阅:在MappingSolrConverter 中)。
@Dynamic
dynamicMappedFieldValues
1.4. Apache Solr 1.4 春季数据中的新增功能
- 升级到最新的 Solr 4.10.x 发行版(需要 Java 7)。
- 添加了对实时获取的支持。
- 获取字段统计信息(最大值、最小值、总和、计数、平均值、缺失值、标准dev 和不同的计算)。
- 用于在文档分数上自动添加投影(请参见:特殊字段)。
@Score
2. 使用 Spring 数据存储库
Spring 数据存储库抽象的目标是显著减少为各种持久性存储实现数据访问层所需的样板代码量。
Spring 数据存储库文档和您的模块 本章解释了 Spring 数据存储库的核心概念和接口。 本章中的信息来自 Spring 数据共享模块。 它使用 Java 持久性 API (JPA) 模块的配置和代码示例。 应将 XML 命名空间声明和要扩展的类型调整为所用特定模块的等效项。“命名空间参考”涵盖了XML配置,所有支持存储库API的Spring Data模块都支持XML配置。“存储库查询关键字”涵盖了存储库抽象通常支持的查询方法关键字。 有关模块特定功能的详细信息,请参阅本文档有关该模块的章节。 |
2.1. 核心概念
Spring 数据存储库抽象中的中心接口是。 它采用要管理的域类以及域类的 ID 类型作为类型参数。 此接口主要充当标记接口,用于捕获要使用的类型,并帮助您发现扩展此接口的接口。 CrudRepository接口为正在管理的实体类提供复杂的 CRUD 功能。Repository
示例 1.界面CrudRepository
public interface CrudRepository<T, ID> extends Repository<T, ID>
<S extends T> S save(S entity);
Optional<T> findById(ID primaryKey);
Iterable<T> findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
// … more functionality omitted.
保存给定的实体。 |
返回由给定 ID 标识的实体。 |
返回所有实体。 |
返回实体数。 |
删除给定实体。 |
指示具有给定 ID 的实体是否存在。 |
我们还提供特定于持久性技术的抽象,例如 asor。 这些接口扩展并公开了底层持久性技术的功能,以及相当通用的持久性技术无关的接口,例如。 |
除此之外,还有一个PagingAndSortingRepository抽象,它添加了额外的方法来简化对实体的分页访问:CrudRepository
示例 2.接口PagingAndSortingRepository
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID>
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
要访问页面大小为 20 的第二页,您可以执行以下操作:User
PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(PageRequest.of(1, 20));
除了查询方法之外,还可以对计数查询和删除查询进行查询派生。 以下列表显示了派生计数查询的接口定义:
例 3.派生计数查询
interface UserRepository extends CrudRepository<User, Long>
long countByLastname(String lastname);
以下清单显示了派生删除查询的接口定义:
例 4.派生删除查询
interface UserRepository extends CrudRepository<User, Long>
long deleteByLastname(String lastname);
List<User> removeByLastname(String lastname);
2.2. 查询方法
标准 CRUD 功能存储库通常对基础数据存储具有查询。 使用 Spring Data,声明这些查询变成了一个四步过程:
- 声明扩展存储库或其子接口之一的接口,并将其键入应处理的域类和 ID 类型,如以下示例所示:
interface PersonRepository extends Repository<Person, Long> …
- 在接口上声明查询方法。
interface PersonRepository extends Repository<Person, Long>
List<Person> findByLastname(String lastname);
- 设置 Spring 以使用JavaConfig或XML 配置为这些接口创建代理实例。
- 要使用 Java 配置,请创建类似于以下内容的类:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config …
- 要使用 XML 配置,请定义类似于以下内容的 Bean:
<?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:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<jpa:repositories base-package="com.acme.repositories"/>
</beans>
此示例中使用 JPA 命名空间。 如果将存储库抽象用于任何其他存储,则需要将其更改为存储模块的相应命名空间声明。 换句话说,您应该交换支持,例如,。jpa
mongodb
另外,请注意,JavaConfig 变体不会显式配置包,因为缺省情况下使用带注释的类的包。 要自定义要扫描的包,请使用特定于数据存储的存储库注释的属性之一。basePackage…
@Enable$storeRepositories
- 注入存储库实例并使用它,如以下示例所示:
class SomeClient
private final PersonRepository repository;
SomeClient(PersonRepository repository)
this.repository = repository;
void doSomething()
List<Person> persons = repository.findByLastname("Matthews");
以下各节详细介绍了每个步骤:
- 定义存储库接口
- 定义查询方法
- 创建存储库实例
- Spring 数据存储库的自定义实现
2.3. 定义仓库接口
要定义存储库接口,首先需要定义特定于域类的存储库接口。 接口必须扩展并键入域类和 ID 类型。 如果要公开该域类型的 CRUD 方法,请扩展代替。Repository
CrudRepository
Repository
2.3.1. 微调存储库定义
通常,存储库界面会扩展、或。 或者,如果您不想扩展 Spring 数据接口,您也可以使用来注释存储库接口。 扩展公开一组完整的方法来操作实体。 如果您希望对要公开的方法有选择性,请将要公开的方法复制到域存储库中。Repository
CrudRepository
PagingAndSortingRepository
@RepositoryDefinition
CrudRepository
CrudRepository
这样做可以让您在提供的 Spring 数据存储库功能之上定义自己的抽象。 |
下面的示例演示如何有选择地公开 CRUD 方法(在本例中为):findById
save
例 5.有选择地公开 CRUD 方法
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID>
Optional<T> findById(ID id);
<S extends T> S save(S entity);
interface UserRepository extends MyBaseRepository<User, Long>
User findByEmailAddress(EmailAddress emailAddress);
在前面的示例中,您为所有域存储库和公开以及定义了通用基本接口。这些方法被路由到 Spring Data 提供的您选择的存储的基本存储库实现中(例如,如果您使用 JPA,则实现是),因为它们与方法签名匹配。 因此,现在可以保存用户,按ID查找单个用户,并触发查询以按电子邮件地址查找。findById(…)
save(…)
SimpleJpaRepository
CrudRepository
UserRepository
Users
中间存储库接口带有注释。 确保将该注释添加到 Spring Data 在运行时不应为其创建实例的所有存储库接口。 |
2.3.2. 使用具有多个 Spring 数据模块的存储库
在应用程序中使用唯一的 Spring 数据模块使事情变得简单,因为定义范围内的所有存储库接口都绑定到 Spring 数据模块。 有时,应用程序需要使用多个 Spring 数据模块。 在这种情况下,存储库定义必须区分持久性技术。 当它在类路径上检测到多个存储库工厂时,Spring Data 进入严格的存储库配置模式。 严格配置使用存储库或域类的详细信息来决定存储库定义的 Spring 数据模块绑定:
- 如果存储库定义扩展了特定于模块的存储库,则它是特定 Spring 数据模块的有效候选者。
- 如果域类使用特定于模块的类型注释进行注释,则它是特定 Spring 数据模块的有效候选者。 Spring Data 模块接受第三方注释(例如 JPA)或提供自己的注释(例如 Spring Data MongoDB 和 Spring Data Elasticsearch)。
@Entity
@Document
以下示例显示了使用特定于模块的接口(在本例中为 JPA)的存储库:
例 6.使用特定于模块的接口的存储库定义
interface MyRepository extends JpaRepository<User, Long>
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> …
interface UserRepository extends MyBaseRepository<User, Long> …
MyRepository
并扩展其类型层次结构。 它们是 Spring Data JPA 模块的有效候选者。UserRepository
JpaRepository
以下示例显示了使用通用接口的存储库:
例 7.使用通用接口的存储库定义
interface AmbiguousRepository extends Repository<User, Long> …
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> …
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> …
AmbiguousRepository
并仅在其类型层次结构中扩展。 虽然这在使用唯一的 Spring 数据模块时很好,但多个模块无法区分这些存储库应该绑定到哪个特定的 Spring 数据。AmbiguousUserRepository
Repository
CrudRepository
以下示例显示了一个使用带有注释的域类的存储库:
例 8.使用带有注释的域类的存储库定义
interface PersonRepository extends Repository<Person, Long> …
@Entity
class Person …
interface UserRepository extends Repository<User, Long> …
@Document
class User …
PersonRepository
引用,它用JPAannotation注释,所以这个存储库显然属于Spring Data JPA.references,它用Spring Data MongoDB的sannotation注释。Person
@Entity
UserRepository
User
@Document
以下错误示例显示了一个使用具有混合注释的域类的存储库:
例 9.使用具有混合注释的域类的存储库定义
interface JpaPersonRepository extends Repository<Person, Long> …
interface MongoDBPersonRepository extends Repository<Person, Long> …
@Entity
@Document
class Person …
这个例子展示了一个同时使用JPA和Spring Data MongoDB注释的域类。 它定义了两个存储库,并且。 一个用于JPA,另一个用于MongoDB使用。 Spring 数据不再能够区分存储库,这会导致未定义的行为。JpaPersonRepository
MongoDBPersonRepository
存储库类型详细信息和区分域类注释用于严格的存储库配置,以识别特定 Spring 数据模块的存储库候选者。 可以对同一域类型使用多个特定于持久性技术的注释,并允许跨多个持久性技术重用域类型。 但是,Spring Data 无法再确定绑定存储库的唯一模块。
区分存储库的最后一种方法是确定存储库基础包的范围。 基本包定义扫描存储库接口定义的起点,这意味着存储库定义位于相应的包中。 默认情况下,注释驱动的配置使用配置类的包。 基于 XML 的配置中的基本包是必需的。
以下示例显示了基本包的注释驱动配置:
例 10.注释驱动的基本包配置
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration …
2.4. 定义查询方法
存储库代理有两种方法可以从方法名称派生特定于存储的查询:
- 通过直接从方法名称派生查询。
- 通过使用手动定义的查询。
可用选项取决于实际商店。 但是,必须有一个策略来决定创建什么实际查询。 下一节介绍可用选项。
2.4.1. 查询查找策略
存储库基础结构可以使用以下策略来解析查询。 使用 XML 配置,您可以通过属性在命名空间中配置策略。 对于 Java 配置,您可以使用注释的属性。 特定数据存储可能不支持某些策略。query-lookup-strategy
queryLookupStrategy
Enable$storeRepositories
-
CREATE
尝试从查询方法名称构造特定于存储的查询。 一般方法是从方法名称中删除一组给定的已知前缀,并分析方法的其余部分。 您可以在“查询创建”中阅读有关查询构造的更多信息。 -
USE_DECLARED_QUERY
尝试查找已声明的查询,如果找不到查询,则会引发异常。 查询可以通过某处的注释定义,也可以通过其他方式声明。 请参阅特定商店的文档以查找该商店的可用选项。 如果存储库基础结构在引导时找不到该方法的声明查询,则会失败。 -
CREATE_IF_NOT_FOUND
(默认值)组合沙。 它首先查找已声明的查询,如果未找到已声明的查询,则会创建一个基于名称的自定义方法查询。 这是默认的查找策略,因此,如果未显式配置任何内容,则使用此方法。 它允许按方法名称快速定义查询,但也允许根据需要引入声明的查询来自定义调整这些查询。CREATE
USE_DECLARED_QUERY
2.4.2. 查询创建
Spring 数据存储库基础结构中内置的查询生成器机制对于构建对存储库实体的约束查询非常有用。
下面的示例演示如何创建多个查询:
例 11.从方法名称创建查询
interface PersonRepository extends Repository<Person, Long>
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
分析查询方法名称分为主语和谓语。 第一部分 (,) 定义查询的主题,第二部分构成谓词。 引言子句(主语)可以包含进一步的表达式。 (或其他引入关键字)之间的任何文本都被视为描述性的,除非使用结果限制关键字之一,例如 ato 在要创建的查询上设置不同的标志或Top/First以限制查询结果。find…Byexists…ByfindByDistinct
附录包含查询方法主题关键字和查询方法谓词关键字的完整列表,包括排序和字母大小写修饰符。 但是,第一个充当分隔符来指示实际条件谓词的开始。 在非常基本的级别上,您可以定义实体属性的条件并将它们与 and 连接起来。By
And
Or
分析方法的实际结果取决于为其创建查询的暂留存储。 但是,有一些一般事项需要注意:
- 表达式通常是属性遍历与可以连接的运算符相结合。 可以将属性表达式与 and 组合在一起。 您还可以获得对运算符的支持,例如,,, 和属性表达式。 支持的运算符可能因数据存储而异,因此请参阅参考文档的相应部分。
AND
OR
Between
LessThan
GreaterThan
Like
- 方法解析器支持为单个属性(例如)或支持忽略大小写的类型的所有属性(通常是实例 — 例如,)设置 anflag。 是否支持忽略案例可能因商店而异,因此请参阅特定于商店的查询方法的参考文档中的相关部分。
IgnoreCase
findByLastnameIgnoreCase(…)
String
findByLastnameAndFirstnameAllIgnoreCase(…)
- 可以通过将 anclause 追加到引用属性的查询方法并提供排序方向 (or) 来应用静态排序。 要创建支持动态排序的查询方法,请参阅 “特殊参数处理”。
OrderBy
Asc
Desc
2.4.3. 属性表达式
属性表达式只能引用托管实体的直接属性,如前面的示例所示。 在创建查询时,已确保分析的属性是托管域类的属性。 但是,也可以通过遍历嵌套属性来定义约束。 请考虑以下方法签名:
List<Person> findByAddressZipCode(ZipCode zipCode);
假设 ahas anwith a。 在这种情况下,该方法将创建属性遍历。 解析算法首先将整个部件 () 解释为属性,并检查域类中是否存在具有该名称(未大写)的属性。 如果算法成功,它将使用该属性。 如果没有,该算法将右侧驼峰案例部分的源拆分为头部和尾部,并尝试找到相应的属性 — 在我们的示例中,and。 如果算法找到具有该头部的属性,它将获取尾部并继续从那里向下构建树,以刚才描述的方式将尾部拆分。 如果第一个拆分不匹配,算法会将拆分点向左移动 (,) 并继续。Person
Address
ZipCode
x.address.zipCode
AddressZipCode
AddressZip
Code
Address
ZipCode
尽管这应该适用于大多数情况,但算法可能会选择错误的属性。 假设该类也有属性。 算法将在第一轮拆分中匹配,选择错误的属性,然后失败(因为类型可能没有属性)。Person
addressZip
addressZip
code
要解决这种歧义,您可以在方法名称中使用手动定义遍历点。 所以我们的方法名称如下:_
List<Person> findByAddress_ZipCode(ZipCode zipCode);
由于我们将下划线字符视为保留字符,因此强烈建议遵循标准的 Java 命名约定(即,不要在属性名称中使用下划线,而是使用驼峰大小写)。
2.4.4. 特殊参数处理
若要处理查询中的参数,请定义方法参数,如前面的示例所示。 除此之外,基础架构还可以识别某些特定类型,例如and,以动态地将分页和排序应用于您的查询。 以下示例演示了这些功能:Pageable
Sort
例 12.使用 、 和 in 查询方法Pageable
Slice
Sort
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
API 获取并期望将非值传递给方法。 如果您不想应用任何排序或分页,请使用和。 |
第一种方法允许您将实例传递给查询方法,以动态地将分页添加到静态定义的查询中。 了解可用元素和页面的总数。 它通过基础结构触发计数查询来计算总数。 由于这可能很昂贵(取决于所使用的商店),因此您可以改为返回 a。 仅知道 next是否可用,这在遍历较大的结果集时可能就足够了。org.springframework.data.domain.Pageable
Page
Slice
Slice
Slice
排序选项也通过实例处理。 如果只需要排序,请向方法添加参数。 如您所见,返回 ais 也是可能的。 在这种情况下,不会创建构建实际实例所需的其他元数据(这反过来意味着不会发出所需的其他计数查询)。 相反,它将查询限制为仅查找给定范围的实体。Pageable
org.springframework.data.domain.Sort
List
Page
要了解整个查询获得的页面数,您必须触发额外的计数查询。 默认情况下,此查询派生自实际触发的查询。 |
分页和排序
可以使用属性名称定义简单的排序表达式。 您可以连接表达式以将多个条件收集到一个表达式中。
例 13.定义排序表达式
Sort sort = Sort.by("firstname").ascending()
.and(Sort.by("lastname").descending());
有关定义排序表达式的更类型安全的方法,请从定义排序表达式的类型开始,并使用方法引用定义要排序的属性。
例 14。使用类型安全的 API 定义排序表达式
TypedSort<Person> person = Sort.sort(Person.class);
Sort sort = person.by(Person::getFirstname).ascending()
.and(person.by(Person::getLastname).descending());
|
如果您的商店实现支持 Querydsl,您还可以使用生成的元模型类型来定义排序表达式:
例 15。使用 Querydsl API 定义排序表达式
QSort sort = QSort.by(QPerson.firstname.asc())
.and(QSort.by(QPerson.lastname.desc()));
2.4.5. 限制查询结果
可以使用 theor关键字来限制查询方法的结果,这些关键字可以互换使用。 您可以附加一个可选的数值来指定要返回的最大结果大小。 如果省略该数字,则假定结果大小为 1。 以下示例演示如何限制查询大小:first
top
top
first
例 16。限制查询的结果大小Top
First
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
限制表达式还支持支持不同查询的数据存储的关键字。 此外,对于将结果集限制为一个实例的查询,支持使用 thekeyword 将结果包装到其中。Distinct
Optional
如果分页或切片应用于限制查询分页(以及可用页数的计算),则会在有限的结果中应用分页或切片。
通过使用参数限制结果和动态排序,可以表示“K”最小元素和“K”最大元素的查询方法。 |
2.4.6. 返回集合或可迭代对象的存储库方法
返回多个结果的查询方法可以使用标准 Java、and。 除此之外,我们还支持返回Spring Data,自定义扩展以及Vavr提供的集合类型。 请参阅解释所有可能的查询方法返回类型的附录。Iterable
List
Set
Streamable
Iterable
使用可流式处理作为查询方法返回类型
您可以将其用作任何集合类型的替代方法。 它提供了访问非并行(缺少)的便捷方法,以及直接覆盖元素并将元素连接到其他元素的能力:Streamable
Iterable
Stream
Iterable
….filter(…)
….map(…)
Streamable
例 17.使用可流式处理合并查询方法结果
interface PersonRepository extends Repository<Person, Long>
Streamable<Person> findByFirstnameContaining(String firstname);
Streamable<Person> findByLastnameContaining(String lastname);
Streamable<Person> result = repository.findByFirstnameContaining("av")
.and(repository.findByLastnameContaining("ea"));
返回自定义可流式传输包装器类型
为集合提供专用包装器类型是一种常用模式,用于为返回多个元素的查询结果提供 API。 通常,通过调用返回类似集合类型的存储库方法并手动创建包装器类型的实例来使用这些类型。 您可以避免该额外步骤,因为如果满足以下条件,Spring Data 允许您将这些包装器类型用作查询方法返回类型:
- 类型实现。
Streamable
- 该类型公开构造函数或名为 dorthat 的静态工厂方法作为参数。
of(…)
valueOf(…)
Streamable
下面的清单显示了一个示例:
class Product
MonetaryAmount getPrice() …
@RequiredArgsConstructor(staticName = "of")
class Products implements Streamable<Product>
private final Streamable<Product> streamable;
public MonetaryAmount getTotal()
return streamable.stream()
.map(Priced::getPrice)
.reduce(Money.of(0), MonetaryAmount::add);
@Override
public Iterator<Product> iterator()
return streamable.iterator();
interface ProductRepository implements Repository<Product, Long>
Products findAllByDescriptionContaining(String text);
公开 API 以访问产品价格的实体。 |
可以使用(使用龙目岛注释创建的工厂方法)构造的包装器类型。 一个标准的构造函数也这样做。 |
包装器类型公开一个额外的 API,计算新值。 |
实现接口并委托给实际结果。 |
该包装器类型可以直接用作查询方法返回类型。 您无需在存储库客户端中查询后返回并手动包装它。 |
支持 Vavr 集合
Vavr是一个包含Java函数式编程概念的库。 它附带一组可用作查询方法返回类型的自定义集合类型,如下表所示:
Vavr 采集类型 | 使用的 Vavr 实现类型 | Spring Data Solr入门小Demo
Spring Boot 在使用 solrj 而不是 spring-boot-starter-data-solr 时会爆炸 |