此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Couchbase 5.5.2! |
Couchbase 存储库
Spring Data 存储库抽象的目标是显着减少为各种持久性存储实现数据访问层所需的样板代码量。
默认情况下,如果作是单文档作且 ID 已知,则由 Key/Value 支持。 对于所有其他作,默认情况下会生成 N1QL 查询,因此必须创建适当的索引才能进行高性能数据访问。
请注意,您可以调整查询所需的一致性(请参阅一致性查询),并拥有由不同存储桶支持的不同存储库(请参阅 [couchbase.repository.multibucket])
配置
虽然始终存在对存储库的支持,但您需要在常规情况下或为特定命名空间启用它们。
如果扩展AbstractCouchbaseConfiguration
,只需使用@EnableCouchbaseRepositories
注解。
它提供了许多可能的选项来缩小或自定义搜索路径,最常见的选项之一是basePackages
.
另请注意,如果您在 spring boot 中运行,则 autoconfig 支持已经为您设置了注释,因此只有在要覆盖默认值时才需要使用它。
@Configuration
@EnableCouchbaseRepositories(basePackages = {"com.couchbase.example.repos"})
public class Config extends AbstractCouchbaseConfiguration {
//...
}
[couchbase.repository.multibucket] 中描述了高级用法。
QueryDSL 配置
Spring Data Couchbase 支持 QueryDSL 来构建类型安全的查询。要启用代码生成,请将CouchbaseAnnotationProcessor
作为注释处理器是必需的。
此外,运行时需要 querydsl-apt 才能在存储库上启用 QueryDSL。
. existing depdendencies including those required for spring-data-couchbase
.
.
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydslVersion}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>annotation-processing</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<proc>only</proc>
<annotationProcessors>
<annotationProcessor>org.springframework.data.couchbase.repository.support.CouchbaseAnnotationProcessor</annotationProcessor>
</annotationProcessors>
<generatedTestSourcesDirectory>target/generated-sources</generatedTestSourcesDirectory>
<compilerArgs>
<arg>-Aquerydsl.logInfo=true</arg>
</compilerArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
dependencies {
annotationProcessor 'com.querydsl:querydsl-apt:${querydslVersion}'
annotationProcessor 'org.springframework.data:spring-data-couchbase'
testAnnotationProcessor 'com.querydsl:querydsl-apt:${querydslVersion}'
testAnnotationProcessor 'org.springframework.data:spring-data-couchbase'
}
tasks.withType(JavaCompile).configureEach {
options.compilerArgs += [
"-processor",
"org.springframework.data.couchbase.repository.support.CouchbaseAnnotationProcessor"]
}
用法
在最简单的情况下,您的存储库将扩展CrudRepository<T, String>
,其中 T 是您要公开的实体。
让我们看一下 UserInfo 的存储库:
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<UserInfo, String> {
}
请注意,这只是一个接口,而不是一个实际的类。 在后台,当上下文初始化时,会创建存储库描述的实际实现,您可以通过常规 bean 访问它们。 这意味着您将保存大量样板代码,同时仍向服务层和应用程序公开完整的 CRUD 语义。
现在,让我们想象一下我们@Autowire
这UserRepository
到使用它的类。
我们有哪些方法可用?
方法 | 描述 |
---|---|
UserInfo save(UserInfo 实体) |
保存给定的实体。 |
Iterable<UserInfo> save(Iterable<UserInfo> 实体) |
保存实体列表。 |
用户信息 findOne(字符串 id) |
通过实体的唯一 ID 查找实体。 |
boolean exists(字符串 id) |
通过其唯一 ID 检查给定实体是否存在。 |
Iterable<UserInfo> findAll() |
在存储桶中按此类型查找所有实体。 |
Iterable<UserInfo>findAll(Iterable<String> ids) |
按此类型和给定的 ID 列表查找所有实体。 |
长计数() |
计算存储桶中的实体数量。 |
void delete(字符串 id) |
按其 ID 删除实体。 |
void delete(UserInfo 实体) |
删除实体。 |
void delete(Iterable<UserInfo> 实体) |
删除所有给定的实体。 |
无效删除全部() |
按类型删除存储桶中的所有实体。 |
现在太棒了! 只需定义一个接口,我们就可以在托管实体之上获得完整的 CRUD 功能。
虽然公开的方法为您提供了多种访问模式,但您经常需要定义自定义模式。 您可以通过向接口添加方法声明来做到这一点,这些声明将自动解析为后台的请求,我们将在下一节中看到。
存储库和查询
基于 N1QL 的查询
前提条件是在将存储实体的存储桶上创建了 PRIMARY INDEX。
这是一个例子:
public interface UserRepository extends CrudRepository<UserInfo, String> {
@Query("#{#n1ql.selectEntity} WHERE role = 'admin' AND #{#n1ql.filter}")
List<UserInfo> findAllAdmins();
List<UserInfo> findByFirstname(String fname);
}
在这里,我们看到了两种 N1QL 支持的查询方式。
第一种方法使用Query
注释以内联提供 N1QL 语句。
SpEL(Spring 表达式语言)由 和 之间的周围 SpEL 表达式块支持。
通过 SpEL 提供了一些特定于 N1QL 的值:#{
}
-
#n1ql.selectEntity
允许轻松确保语句将选择构建完整实体所需的所有字段(包括文档 ID 和 CAS 值)。 -
#n1ql.filter
在 WHERE 子句中添加一个条件,将实体类型与 Spring Data 用于存储类型信息的字段匹配。 -
#n1ql.bucket
将替换为存储实体的存储桶的名称,以反引号转义。 -
#n1ql.scope
将替换为实体存储的范围的名称,并以反引号转义。 -
#n1ql.collection
将替换为存储实体的集合的名称,以反引号转义。 -
#n1ql.fields
将被重建实体所需的字段列表(例如,对于 SELECT 子句)所取代。 -
#n1ql.delete
将被delete from
陈述。 -
#n1ql.returning
将被重建实体所需的返回子句所取代。
我们建议您始终使用selectEntity SpEL 和带有filter SpEL(否则您的查询可能会受到来自其他存储库的实体的影响)。 |
基于字符串的查询支持参数化查询。
您可以使用像“$1”这样的位置占位符,在这种情况下,每个方法参数将按顺序映射到$1
,$2
,$3
…或者,您可以使用“$someString”语法使用命名占位符。
方法参数将使用参数名称与其相应的占位符匹配,可以通过注释每个参数来覆盖该占位符(除了Pageable
或Sort
) 替换为@Param
(例如。@Param("someString")
).
您不能在查询中混合使用这两种方法,并且会得到一个IllegalArgumentException
如果你这样做的话。
请注意,您可以混合使用 N1QL 占位符和 SpEL。N1QL 占位符仍将考虑所有方法参数,因此请务必使用正确的索引,如以下示例所示:
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND #{[0]} = $2")
public List<User> findUsersByDynamicCriteria(String criteriaField, Object criteriaValue)
这允许您生成与 eg.AND name = "someName"
或AND age = 3
,使用单个方法声明。
您还可以在 N1QL 查询中进行单一投影(前提是它只选择一个字段并仅返回一个结果,通常是像COUNT
,AVG
,MAX
…).
这样的投影将具有简单的返回类型,例如long
,boolean
或String
.
这不适用于对 DTO 的预测。
另一个例子:
#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND test = $1
相当于
SELECT #{#n1ql.fields} FROM #{#n1ql.collection} WHERE #{#n1ql.filter} AND test = $1
第二种方法使用 Spring-Data 的查询派生机制,从方法名称和参数构建 N1QL 查询。
这将生成如下所示的查询:SELECT … FROM … WHERE firstName = "valueOfFnameAtRuntime"
.
您可以结合这些标准,甚至可以使用类似countByFirstname
或名称为findFirst3ByLastname
…
实际上,生成的 N1QL 查询还将包含一个额外的 N1QL 条件,以便仅选择与存储库的实体类匹配的文档。 |
支持大多数 Spring-Data 关键字: .@Query (N1QL) 方法名称中支持的关键字
关键词 | 样本 | N1QL WHERE 子句片段 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
您可以通过此方法同时使用计数查询和 [repositories.limit-query-result] 功能。
对于 N1QL,存储库的另一个可能接口是PagingAndSortingRepository
一个(扩展CrudRepository
).
它添加了两种方法:
方法 | 描述 |
---|---|
Iterable<T> findAll(排序排序); |
允许在对其属性之一进行排序时检索所有相关实体。 |
Page<T> findAll(可分页可分页); |
允许检索页面中的实体。返回的 |
您还可以使用Page 和Slice 作为方法返回类型以及 N1QL 支持的存储库。 |
如果可分页和排序参数与内联查询一起使用,则内联查询本身中不应有任何 order by、limit 或 offset 子句,否则服务器将拒绝格式错误的查询。 |
自动索引管理
默认情况下,用户应为其查询创建和管理最佳索引。特别是在开发的早期阶段,自动创建索引以快速启动可以派上用场。
对于 N1QL,提供了以下注释,需要附加到实体(在类或字段上):
-
@QueryIndexed
:放置在字段上以指示该字段应是索引的一部分 -
@CompositeQueryIndex
:放置在类上以指示应在多个字段(复合)上创建索引。 -
@CompositeQueryIndexes
:如果多个CompositeQueryIndex
应该创建,则此注释将采用它们的列表。
例如,这是在实体上定义复合索引的方式:
@Document
@CompositeQueryIndex(fields = {"id", "name desc"})
public class Airline {
@Id
String id;
@QueryIndexed
String name;
@PersistenceConstructor
public Airline(String id, String name) {
this.id = id;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
默认情况下,索引创建处于禁用状态。如果要启用它,则需要在配置上覆盖它:
@Override
protected boolean autoIndexCreation() {
return true;
}
一致性查询
默认情况下,使用 N1QL 的存储库查询使用NOT_BOUNDED
扫描一致性。这意味着结果会快速返回,但索引中的数据可能尚未包含以前写入的作(称为最终一致性)的数据。如果您需要查询的“ready your own write”语义,则需要使用@ScanConsistency
注解。这是一个例子:
@Repository
public interface AirportRepository extends PagingAndSortingRepository<Airport, String> {
@Override
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Iterable<Airport> findAll();
}
DTO 预测
Spring Data Repositories 在使用查询方法时通常返回域模型。 但是,有时,由于各种原因,您可能需要更改该模型的视图。 在本节中,您将学习如何定义投影以提供简化和简化的资源视图。
查看以下域模型:
@Entity
public class Person {
@Id @GeneratedValue
private Long id;
private String firstName, lastName;
@OneToOne
private Address address;
…
}
@Entity
public class Address {
@Id @GeneratedValue
private Long id;
private String street, state, country;
…
}
这Person
有几个属性:
-
id
是主键 -
firstName
和lastName
是数据属性 -
address
是指向另一个域对象的链接
现在假设我们创建一个相应的存储库,如下所示:
interface PersonRepository extends CrudRepository<Person, Long> {
Person findPersonByFirstName(String firstName);
}
Spring Data 将返回域对象,包括其所有属性。
有两个选项可以检索address
属性。
一种选择是为Address
像这样的对象:
interface AddressRepository extends CrudRepository<Address, Long> {}
在这种情况下,使用PersonRepository
仍会返回整个Person
对象。
用AddressRepository
将仅返回Address
.
但是,如果您不想暴露address
细节?您可以通过定义一个或多个投影来为存储库服务的使用者提供替代方案。
interface NoAddresses { (1)
String getFirstName(); (2)
String getLastName(); (3)
}
此投影具有以下详细信息:
1 | 一个纯 Java 接口,使其具有声明性。 |
2 | 导出firstName . |
3 | 导出lastName . |
这NoAddresses
投影只有firstName
和lastName
这意味着它不会提供任何地址信息。在这种情况下,查询方法定义返回NoAdresses
而不是Person
.
interface PersonRepository extends CrudRepository<Person, Long> {
NoAddresses findByFirstName(String firstName);
}
投影声明底层类型与与公开属性相关的方法签名之间的契约。因此,需要根据底层类型的属性名称命名 getter 方法。如果底层属性名为firstName
,则必须将 getter 方法命名为getFirstName
否则,Spring Data无法查找源属性。