此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Couchbase 5.5.2spring-doc.cadn.net.cn

Couchbase 存储库

Spring Data 存储库抽象的目标是显着减少为各种持久性存储实现数据访问层所需的样板代码量。spring-doc.cadn.net.cn

默认情况下,如果作是单文档作且 ID 已知,则由 Key/Value 支持。 对于所有其他作,默认情况下会生成 N1QL 查询,因此必须创建适当的索引才能进行高性能数据访问。spring-doc.cadn.net.cn

请注意,您可以调整查询所需的一致性(请参阅一致性查询),并拥有由不同存储桶支持的不同存储库(请参阅 [couchbase.repository.multibucket])spring-doc.cadn.net.cn

配置

虽然始终存在对存储库的支持,但您需要在常规情况下或为特定命名空间启用它们。 如果扩展AbstractCouchbaseConfiguration,只需使用@EnableCouchbaseRepositories注解。 它提供了许多可能的选项来缩小或自定义搜索路径,最常见的选项之一是basePackages.spring-doc.cadn.net.cn

另请注意,如果您在 spring boot 中运行,则 autoconfig 支持已经为您设置了注释,因此只有在要覆盖默认值时才需要使用它。spring-doc.cadn.net.cn

示例 1.基于注释的存储库设置
@Configuration
@EnableCouchbaseRepositories(basePackages = {"com.couchbase.example.repos"})
public class Config extends AbstractCouchbaseConfiguration {
    //...
}

QueryDSL 配置

Spring Data Couchbase 支持 QueryDSL 来构建类型安全的查询。要启用代码生成,请将CouchbaseAnnotationProcessor作为注释处理器是必需的。 此外,运行时需要 querydsl-apt 才能在存储库上启用 QueryDSL。spring-doc.cadn.net.cn

示例 2.Maven 配置示例
    . 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>
示例 3.Gradle 配置示例
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 的存储库:spring-doc.cadn.net.cn

示例 4.UserInfo 存储库
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<UserInfo, String> {
}

请注意,这只是一个接口,而不是一个实际的类。 在后台,当上下文初始化时,会创建存储库描述的实际实现,您可以通过常规 bean 访问它们。 这意味着您将保存大量样板代码,同时仍向服务层和应用程序公开完整的 CRUD 语义。spring-doc.cadn.net.cn

现在,让我们想象一下我们@AutowireUserRepository到使用它的类。 我们有哪些方法可用?spring-doc.cadn.net.cn

表 1.UserRepository 上公开的方法
方法 描述

UserInfo save(UserInfo 实体)spring-doc.cadn.net.cn

保存给定的实体。spring-doc.cadn.net.cn

Iterable<UserInfo> save(Iterable<UserInfo> 实体)spring-doc.cadn.net.cn

保存实体列表。spring-doc.cadn.net.cn

用户信息 findOne(字符串 id)spring-doc.cadn.net.cn

通过实体的唯一 ID 查找实体。spring-doc.cadn.net.cn

boolean exists(字符串 id)spring-doc.cadn.net.cn

通过其唯一 ID 检查给定实体是否存在。spring-doc.cadn.net.cn

Iterable<UserInfo> findAll()spring-doc.cadn.net.cn

在存储桶中按此类型查找所有实体。spring-doc.cadn.net.cn

Iterable<UserInfo>findAll(Iterable<String> ids)spring-doc.cadn.net.cn

按此类型和给定的 ID 列表查找所有实体。spring-doc.cadn.net.cn

长计数()spring-doc.cadn.net.cn

计算存储桶中的实体数量。spring-doc.cadn.net.cn

void delete(字符串 id)spring-doc.cadn.net.cn

按其 ID 删除实体。spring-doc.cadn.net.cn

void delete(UserInfo 实体)spring-doc.cadn.net.cn

删除实体。spring-doc.cadn.net.cn

void delete(Iterable<UserInfo> 实体)spring-doc.cadn.net.cn

删除所有给定的实体。spring-doc.cadn.net.cn

无效删除全部()spring-doc.cadn.net.cn

按类型删除存储桶中的所有实体。spring-doc.cadn.net.cn

现在太棒了! 只需定义一个接口,我们就可以在托管实体之上获得完整的 CRUD 功能。spring-doc.cadn.net.cn

虽然公开的方法为您提供了多种访问模式,但您经常需要定义自定义模式。 您可以通过向接口添加方法声明来做到这一点,这些声明将自动解析为后台的请求,我们将在下一节中看到。spring-doc.cadn.net.cn

存储库和查询

基于 N1QL 的查询

前提条件是在将存储实体的存储桶上创建了 PRIMARY INDEX。spring-doc.cadn.net.cn

这是一个例子:spring-doc.cadn.net.cn

示例 5.带有 N1QL 查询的扩展 UserInfo 存储库
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 支持的查询方式。spring-doc.cadn.net.cn

第一种方法使用Query注释以内联提供 N1QL 语句。 SpEL(Spring 表达式语言)由 和 之间的周围 SpEL 表达式块支持。 通过 SpEL 提供了一些特定于 N1QL 的值:#{}spring-doc.cadn.net.cn

  • #n1ql.selectEntity允许轻松确保语句将选择构建完整实体所需的所有字段(包括文档 ID 和 CAS 值)。spring-doc.cadn.net.cn

  • #n1ql.filter在 WHERE 子句中添加一个条件,将实体类型与 Spring Data 用于存储类型信息的字段匹配。spring-doc.cadn.net.cn

  • #n1ql.bucket将替换为存储实体的存储桶的名称,以反引号转义。spring-doc.cadn.net.cn

  • #n1ql.scope将替换为实体存储的范围的名称,并以反引号转义。spring-doc.cadn.net.cn

  • #n1ql.collection将替换为存储实体的集合的名称,以反引号转义。spring-doc.cadn.net.cn

  • #n1ql.fields将被重建实体所需的字段列表(例如,对于 SELECT 子句)所取代。spring-doc.cadn.net.cn

  • #n1ql.delete将被delete from陈述。spring-doc.cadn.net.cn

  • #n1ql.returning将被重建实体所需的返回子句所取代。spring-doc.cadn.net.cn

我们建议您始终使用selectEntitySpEL 和带有filterSpEL(否则您的查询可能会受到来自其他存储库的实体的影响)。

基于字符串的查询支持参数化查询。 您可以使用像“$1”这样的位置占位符,在这种情况下,每个方法参数将按顺序映射到$1,$2,$3…​或者,您可以使用“$someString”语法使用命名占位符。 方法参数将使用参数名称与其相应的占位符匹配,可以通过注释每个参数来覆盖该占位符(除了PageableSort) 替换为@Param(例如。@Param("someString")). 您不能在查询中混合使用这两种方法,并且会得到一个IllegalArgumentException如果你这样做的话。spring-doc.cadn.net.cn

请注意,您可以混合使用 N1QL 占位符和 SpEL。N1QL 占位符仍将考虑所有方法参数,因此请务必使用正确的索引,如以下示例所示:spring-doc.cadn.net.cn

示例 6.混合 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,使用单个方法声明。spring-doc.cadn.net.cn

您还可以在 N1QL 查询中进行单一投影(前提是它只选择一个字段并仅返回一个结果,通常是像COUNT,AVG,MAX…​). 这样的投影将具有简单的返回类型,例如long,booleanString. 这适用于对 DTO 的预测。spring-doc.cadn.net.cn

另一个例子:
#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND test = $1
相当于
SELECT #{#n1ql.fields} FROM #{#n1ql.collection} WHERE #{#n1ql.filter} AND test = $1spring-doc.cadn.net.cn

SpEL 与 Spring Security 的实际应用

当您想要根据其他 Spring 组件(如 Spring Security)注入的数据进行查询时,SpEL 会很有用。 以下是扩展 SpEL 上下文以访问此类外部数据所需执行的作。spring-doc.cadn.net.cn

首先,您需要实现一个EvaluationContextExtension(使用支持类如下):spring-doc.cadn.net.cn

class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {

  @Override
  public String getExtensionId() {
    return "security";
  }

  @Override
  public SecurityExpressionRoot getRootObject() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    return new SecurityExpressionRoot(authentication) {};
  }
}

然后,您需要做的就是在配置中声明相应的 bean,以便 Spring Data Couchbase 能够访问关联的 SpEL 值:spring-doc.cadn.net.cn

@Bean
EvaluationContextExtension securityExtension() {
    return new SecurityEvaluationContextExtension();
}

这对于根据连接用户的角色制作查询可能很有用,例如:spring-doc.cadn.net.cn

@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND " +
"role = '?#{hasRole('ROLE_ADMIN') ? 'public_admin' : 'admin'}'")
List<UserInfo> findAllAdmins(); //only ROLE_ADMIN users will see hidden admins

删除查询示例:spring-doc.cadn.net.cn

@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} AND " +
"username = $1 #{#n1ql.returning}")
UserInfo removeUser(String username);

第二种方法使用 Spring-Data 的查询派生机制,从方法名称和参数构建 N1QL 查询。 这将生成如下所示的查询:SELECT …​ FROM …​ WHERE firstName = "valueOfFnameAtRuntime". 您可以结合这些标准,甚至可以使用类似countByFirstname或名称为findFirst3ByLastname…​spring-doc.cadn.net.cn

实际上,生成的 N1QL 查询还将包含一个额外的 N1QL 条件,以便仅选择与存储库的实体类匹配的文档。

支持大多数 Spring-Data 关键字: .@Query (N1QL) 方法名称中支持的关键字spring-doc.cadn.net.cn

关键词 样本 N1QL WHERE 子句片段

Andspring-doc.cadn.net.cn

findByLastnameAndFirstnamespring-doc.cadn.net.cn

lastName = a AND firstName = bspring-doc.cadn.net.cn

Orspring-doc.cadn.net.cn

findByLastnameOrFirstnamespring-doc.cadn.net.cn

lastName = a OR firstName = bspring-doc.cadn.net.cn

Is,Equalsspring-doc.cadn.net.cn

findByField,findByFieldEqualsspring-doc.cadn.net.cn

field = aspring-doc.cadn.net.cn

IsNot,Notspring-doc.cadn.net.cn

findByFieldIsNotspring-doc.cadn.net.cn

field != aspring-doc.cadn.net.cn

Betweenspring-doc.cadn.net.cn

findByFieldBetweenspring-doc.cadn.net.cn

field BETWEEN a AND bspring-doc.cadn.net.cn

IsLessThan,LessThan,IsBefore,Beforespring-doc.cadn.net.cn

findByFieldIsLessThan,findByFieldBeforespring-doc.cadn.net.cn

field < aspring-doc.cadn.net.cn

IsLessThanEqual,LessThanEqualspring-doc.cadn.net.cn

findByFieldIsLessThanEqualspring-doc.cadn.net.cn

field ⇐ aspring-doc.cadn.net.cn

IsGreaterThan,GreaterThan,IsAfter,Afterspring-doc.cadn.net.cn

findByFieldIsGreaterThan,findByFieldAfterspring-doc.cadn.net.cn

field > aspring-doc.cadn.net.cn

IsGreaterThanEqual,GreaterThanEqualspring-doc.cadn.net.cn

findByFieldGreaterThanEqualspring-doc.cadn.net.cn

field >= aspring-doc.cadn.net.cn

IsNullspring-doc.cadn.net.cn

findByFieldIsNullspring-doc.cadn.net.cn

field IS NULLspring-doc.cadn.net.cn

IsNotNull,NotNullspring-doc.cadn.net.cn

findByFieldIsNotNullspring-doc.cadn.net.cn

field IS NOT NULLspring-doc.cadn.net.cn

IsLike,Likespring-doc.cadn.net.cn

findByFieldLikespring-doc.cadn.net.cn

field LIKE "a"- a 应该是包含 % 和 _ 的字符串(匹配 n 个字符和 1 个字符)spring-doc.cadn.net.cn

IsNotLike,NotLikespring-doc.cadn.net.cn

findByFieldNotLikespring-doc.cadn.net.cn

field NOT LIKE "a"- a 应该是包含 % 和 _ 的字符串(匹配 n 个字符和 1 个字符)spring-doc.cadn.net.cn

IsStartingWith,StartingWith,StartsWithspring-doc.cadn.net.cn

findByFieldStartingWithspring-doc.cadn.net.cn

field LIKE "a%"- a 应该是 String 前缀spring-doc.cadn.net.cn

IsEndingWith,EndingWith,EndsWithspring-doc.cadn.net.cn

findByFieldEndingWithspring-doc.cadn.net.cn

field LIKE "%a"- a 应该是 String 后缀spring-doc.cadn.net.cn

IsContaining,Containing,Containsspring-doc.cadn.net.cn

findByFieldContainsspring-doc.cadn.net.cn

field LIKE "%a%"- a 应该是一个字符串spring-doc.cadn.net.cn

IsNotContaining,NotContaining,NotContainsspring-doc.cadn.net.cn

findByFieldNotContainingspring-doc.cadn.net.cn

field NOT LIKE "%a%"- a 应该是一个字符串spring-doc.cadn.net.cn

IsIn,Inspring-doc.cadn.net.cn

findByFieldInspring-doc.cadn.net.cn

field IN array- 请注意,下一个参数值(如果是集合/数组,则为其子参数)应兼容存储在JsonArray)spring-doc.cadn.net.cn

IsNotIn,NotInspring-doc.cadn.net.cn

findByFieldNotInspring-doc.cadn.net.cn

field NOT IN array- 请注意,下一个参数值(如果是集合/数组,则为其子参数)应兼容存储在JsonArray)spring-doc.cadn.net.cn

IsTrue,Truespring-doc.cadn.net.cn

findByFieldIsTruespring-doc.cadn.net.cn

field = TRUEspring-doc.cadn.net.cn

IsFalse,Falsespring-doc.cadn.net.cn

findByFieldFalsespring-doc.cadn.net.cn

field = FALSEspring-doc.cadn.net.cn

MatchesRegex,Matches,Regexspring-doc.cadn.net.cn

findByFieldMatchesspring-doc.cadn.net.cn

REGEXP_LIKE(field, "a")- 请注意,这里忽略了 ignoreCase,a 是 String 形式的正则表达式spring-doc.cadn.net.cn

Existsspring-doc.cadn.net.cn

findByFieldExistsspring-doc.cadn.net.cn

field IS NOT MISSING- 用于验证 JSON 是否包含此属性spring-doc.cadn.net.cn

OrderByspring-doc.cadn.net.cn

findByFieldOrderByLastnameDescspring-doc.cadn.net.cn

field = a ORDER BY lastname DESCspring-doc.cadn.net.cn

IgnoreCasespring-doc.cadn.net.cn

findByFieldIgnoreCasespring-doc.cadn.net.cn

LOWER(field) = LOWER("a")- a 必须是字符串spring-doc.cadn.net.cn

您可以通过此方法同时使用计数查询和 [repositories.limit-query-result] 功能。spring-doc.cadn.net.cn

对于 N1QL,存储库的另一个可能接口是PagingAndSortingRepository一个(扩展CrudRepository). 它添加了两种方法:spring-doc.cadn.net.cn

表 2.PagingAndSortingRepository 上公开的方法
方法 描述

Iterable<T> findAll(排序排序);spring-doc.cadn.net.cn

允许在对其属性之一进行排序时检索所有相关实体。spring-doc.cadn.net.cn

Page<T> findAll(可分页可分页);spring-doc.cadn.net.cn

允许检索页面中的实体。返回的Page允许轻松获取下一页的Pageable以及项目列表。对于第一次调用,请使用new PageRequest(0, pageSize)作为 Pageable。spring-doc.cadn.net.cn

您还可以使用PageSlice作为方法返回类型以及 N1QL 支持的存储库。
如果可分页和排序参数与内联查询一起使用,则内联查询本身中不应有任何 order by、limit 或 offset 子句,否则服务器将拒绝格式错误的查询。

自动索引管理

默认情况下,用户应为其查询创建和管理最佳索引。特别是在开发的早期阶段,自动创建索引以快速启动可以派上用场。spring-doc.cadn.net.cn

对于 N1QL,提供了以下注释,需要附加到实体(在类或字段上):spring-doc.cadn.net.cn

  • @QueryIndexed:放置在字段上以指示该字段应是索引的一部分spring-doc.cadn.net.cn

  • @CompositeQueryIndex:放置在类上以指示应在多个字段(复合)上创建索引。spring-doc.cadn.net.cn

  • @CompositeQueryIndexes:如果多个CompositeQueryIndex应该创建,则此注释将采用它们的列表。spring-doc.cadn.net.cn

例如,这是在实体上定义复合索引的方式:spring-doc.cadn.net.cn

示例 7.两个字段上的复合索引,具有排序
@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;
	}

}

默认情况下,索引创建是禁用的。如果要启用它,则需要在配置上覆盖它:spring-doc.cadn.net.cn

示例 8.启用自动索引创建
@Override
protected boolean autoIndexCreation() {
 return true;
}

一致性查询

默认情况下,使用 N1QL 的存储库查询使用NOT_BOUNDED扫描一致性。这意味着结果会快速返回,但索引中的数据可能尚未包含以前写入的作(称为最终一致性)的数据。如果您需要查询的“ready your own write”语义,则需要使用@ScanConsistency注解。这是一个例子:spring-doc.cadn.net.cn

示例 9.使用不同的扫描一致性
@Repository
public interface AirportRepository extends PagingAndSortingRepository<Airport, String> {

	@Override
	@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
	Iterable<Airport> findAll();

}

DTO 预测

Spring Data Repositories 在使用查询方法时通常返回域模型。 但是,有时,由于各种原因,您可能需要更改该模型的视图。 在本节中,您将学习如何定义投影以提供简化和简化的资源视图。spring-doc.cadn.net.cn

查看以下域模型:spring-doc.cadn.net.cn

@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有几个属性:spring-doc.cadn.net.cn

现在假设我们创建一个相应的存储库,如下所示:spring-doc.cadn.net.cn

interface PersonRepository extends CrudRepository<Person, Long> {

  Person findPersonByFirstName(String firstName);
}

Spring Data 将返回域对象,包括其所有属性。 有两个选项可以检索address属性。 一种选择是为Address像这样的对象:spring-doc.cadn.net.cn

interface AddressRepository extends CrudRepository<Address, Long> {}

在这种情况下,使用PersonRepository仍会返回整个Person对象。 用AddressRepository将仅返回Address.spring-doc.cadn.net.cn

但是,如果您不想暴露address细节? 您可以通过定义一个或多个投影来为存储库服务的使用者提供替代方法。spring-doc.cadn.net.cn

示例 10.简单投影
interface NoAddresses {  (1)

  String getFirstName(); (2)

  String getLastName();  (3)
}

此投影具有以下详细信息:spring-doc.cadn.net.cn

1 一个纯 Java 接口,使其具有声明性。
2 导出firstName.
3 导出lastName.

NoAddresses投影只有firstNamelastName这意味着它不会提供任何地址信息。 在这种情况下,查询方法定义返回NoAdresses而不是Person.spring-doc.cadn.net.cn

interface PersonRepository extends CrudRepository<Person, Long> {

  NoAddresses findByFirstName(String firstName);
}

投影声明基础类型与与公开属性相关的方法签名之间的协定。 因此,需要根据底层类型的属性名称命名 getter 方法。 如果基础属性名为firstName,则必须将 getter 方法命名为getFirstName否则,Spring Data无法查找源属性。spring-doc.cadn.net.cn