Spring Data(数据) Couchbase

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Data(数据) Couchbase相关的知识,希望对你有一定的参考价值。

版本 5.0.0

Spring

本参考文档描述了 Spring Data Couchbase 库的一般用法。

项目信息

  • 版本控制 -https://github.com/spring-projects/spring-data-couchbase
  • 错误跟踪器 -https://jira.springsource.org/browse/DATACOUCH
  • 发布存储库 -https://repo.spring.io/libs-release
  • 里程碑存储库 -https://repo.spring.io/libs-milestone
  • 快照存储库 -https://repo.spring.io/libs-snapshot

从 Spring Data Couchbase 3.x 迁移到 4.x

本章简要介绍了 4.x 中引入了哪些重大更改,并简要概述了迁移时要考虑的事项。

请注意,最低 Couchbase 服务器版本已隐含地提升到 5.5 及更高版本,我们建议至少运行 6.0.x。

配置

由于主要目标是从 Java SDK 2 迁移到 3,因此配置已更改以适应新的 SDK,并且从长远来看,还可以为范围和集合做好准备(但它仍然可以在没有集合支持的情况下使用)。

XML 配置支持已被删除,因此仅支持基于 java/注释的配置。

您的配置仍然需要扩展,但由于 RBAC(基于角色的访问控制)现在是必需的,因此需要覆盖不同的属性才能进行配置:,,和。如果要选择性地使用非默认作用域,可以重写该方法。请注意,如果要使用基于证书的身份验证或需要自定义密码身份验证,则可以重写该方法以执行此任务。​​AbstractCouchbaseConfiguration​​​​getConnectionString​​​​getUserName​​​​getPassword​​​​getBucketName​​​​getScopeName​​​​authenticator​

新 SDK 仍具有用于配置它的环境,因此您可以覆盖该方法并在需要时提供自定义配置。​​configureEnvironment​

有关更多信息,请参阅安装和配置。

Spring 引导版本兼容性

Spring Boot 2.3.x 或更高版本取决于 Spring Data Couchbase 4.x。早期版本的 Couchbase 不可用,因为 SDK 2 和 3 不能位于同一类路径上。

实体

如何处理实体没有改变,尽管由于SDK现在不再提供注释,因此仅支持与Spring-Data相关的注释。

具体说来:

  • ​com.couchbase.client.java.repository.annotation.Id​​成为import org.springframework.data.annotation.Id
  • ​com.couchbase.client.java.repository.annotation.Field​​成为import org.springframework.data.couchbase.core.mapping.Field

注释保持不变。​​org.springframework.data.couchbase.core.mapping.Document​

有关详细信息,请参阅实体建模。

自动索引管理

自动索引管理已经过重新设计,允许更灵活的索引编制。引入了新的注释,并删除了旧的注释。​​@ViewIndexed​​​​@N1qlSecondaryIndexed​​​​@N1qlPrimaryIndexed​

有关详细信息,请参阅自动索引管理。

模板和反应式模板

由于Couchbase SDK 3删除了对的支持,而是增加了对的支持,因此都可以直接从中访问。​​RxJava​​​​Reactor​​​​couchbaseTemplate​​​​reactiveCouchbaseTemplate​​​​AbstractCouchbaseConfiguration​

该模板已经过彻底修改,因此它现在使用流畅的 API 进行配置,而不是许多方法重载。这样做的好处是,将来我们能够扩展功能,而不必引入越来越多的重载,从而使导航变得复杂。

下表描述了 3.x 中的方法名称,并将它们与 4.x 等效项进行了比较:

表 1.模板方法比较

SDC 3.x

SDC 4.x

upsertById

插入

插入按ID

更新

replaceById

查找按ID

查找按ID

查找按视图

(已删除)

查找按空间视图

(已删除)

findByN1QL

查找按查询

findByN1QLProjection

查找按查询

查询N1QL

(直接调用SDK)

存在

existById

删除

删除按ID

执行

(直接调用SDK)

此外,还添加了以下在 3.x 中不可用的方法:

表 2.4.x 中的模板添加

名字

描述

removeByQuery

允许通过 N1QL 查询删除实体

查找比分析

通过分析服务执行查找

findFromReplicasById

像findById,但考虑了副本

我们尝试将 API 与底层 SDK 语义更紧密地统一和对齐,以便它们更易于关联和导航。

有关详细信息,请参阅模板和直接操作。

存储库和查询

  • ​org.springframework.data.couchbase.core.query.Query​​成为org.springframework.data.couchbase.repository.Query
  • ​org.springframework.data.couchbase.repository.ReactiveCouchbaseSortingRepository​​已被删除。考虑延长ReactiveSortingRepositoryReactiveCouchbaseRepository
  • ​org.springframework.data.couchbase.repository.CouchbasePagingAndSortingRepository​​已被删除。考虑延长PagingAndSortingRepositoryCouchbaseRepository


删除了对视图的支持,N1QL 查询现在是所有自定义存储库方法以及默认情况下内置方法的一等公民。

与以前的版本相比,行为本身在查询派生应该如何工作方面没有变化。如果您遇到任何过去有效的查询,现在不再有效,请告诉我们。

可以通过 newannotation 覆盖 N1QL 查询的默认扫描一致性。​​ScanConsistency​

该方法也已删除。您仍然可以通过类器从本机 Java SDK 访问所有方法:​​getCouchbaseOperations()​​​​CouchbaseTemplate​​​​Cluster​

@Service
public class MyService

@Autowired
private CouchbaseTemplate couchbaseTemplate;

@Autowired
private Cluster cluster;

有关更多信息,请参阅Couchbase 存储库。

全文搜索 (FTS)

FTS API 已简化,现在可以通过类访问:​​Cluster​

@Service
public class MyService

@Autowired
private Cluster cluster;

public void myMethod()
try
final SearchResult result = cluster
.searchQuery("index", SearchQuery.queryString("query"));

for (SearchRow row : result.rows())
System.out.println("Found row: " + row);


System.out.println("Reported total rows: "
+ result.metaData().metrics().totalRows());
catch (CouchbaseException ex)
ex.printStackTrace();


有关详细信息,请参阅 FTS 文档。

1. 升级弹簧数据

有关如何从早期版本的 Spring 数据升级的说明在项目wiki 上提供。 按照发行说明部分中的链接查找要升级到的版本。

升级说明始终是发行说明中的第一项。如果您落后多个版本,请确保您还查看了您跳转的版本的发行说明。

Spring

2. 安装和配置

本章介绍使用库时所需的常见安装和配置步骤。

2.1. 安装

所有用于生产使用的版本都分布在Maven Central和Spring版本存储库中。 因此,可以像任何其他 maven 依赖项一样包含该库:

例 1.包括通过 maven 的依赖关系

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchbase</artifactId>
<version>5.0.0</version>
</dependency>

这将引入几个依赖项,包括底层的Couchbase Java SDK,常见的Spring依赖项以及作为JSON映射基础结构的Jackson。

您还可以从 Spring 快照存储库( https://repo.spring.io/libs-snapshot ) 中获取快照,从Spring 里程碑存储库( https://repo.spring.io/libs-milestone ) 中获取里程碑版本。 下面是有关如何使用当前 SNAPSHOT 依赖项的示例:

例 2.使用快照版本

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchbase</artifactId>
<version>$version-SNAPSHOT</version>
</dependency>

<repository>
<id>spring-libs-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>

一旦在类路径上有了所有需要的依赖关系,就可以开始配置它了。 仅支持 Java 配置(XML 配置已在 4.0 中删除)。

2.2. 基于注释的配置(“JavaConfig”)

首先,您需要做的就是子分支并实现抽象方法。​​AbstractCouchbaseConfiguration​

例 3.扩展​​AbstractCouchbaseConfiguration​

@Configuration
public class Config extends AbstractCouchbaseConfiguration

@Override
public String getConnectionString()
return "couchbase://127.0.0.1";


@Override
public String getUserName()
return "Administrator";


@Override
public String getPassword()
return "password";


@Override
public String getBucketName()
return "travel-sample";

连接字符串由主机列表和可选方案 () 组成,如上面的代码所示。 您只需要提供一个要引导到的 Couchbase 节点列表(用 a 分隔)。请注意,虽然一个 主机在开发中就足够了,建议在这里添加 3 到 5 个引导节点。Couchbase 将拾取所有节点 自动从群集,但可能是您提供的唯一节点遇到问题 您正在启动应用程序。​​couchbase://​​​​,​

通过 RBAC(基于角色的访问控制)在 Couchbase Server 群集中配置的 Theandare。 反映要用于此配置的存储桶。​​userName​​​​password​​​​bucketName​

此外,可以通过重写 ato 返回已配置的方法来调整 SDK 环境。​​configureEnvironment​​​​ClusterEnvironment.Builder​​​​ClusterEnvironment​

从此配置中,可以自定义和覆盖更多内容作为自定义 Bean(例如存储库, 验证和自定义转换器)。

如果使用 and,则可能会遇到前缀为前缀的字段的问题。 由于 Spring Data Couchbase 默认将类型信息存储为 aattribute,这可能会有问题。 覆盖(例如返回)以更改 所述属性的名称。​​SyncGateway​​​​CouchbaseMobile​​​​_​​​​_class​​​​typeKey()​​​​MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE​

如果启动应用程序,则应在日志中看到 Couchbase INFO 级别日志记录,指示底层 Couchbase Java SDK正在连接到数据库。如果报告了任何错误,请确保给定的凭据 并且主机信息正确。

3. 建模实体

本章介绍如何对实体进行建模,并解释它们在 Couchbase 服务器本身中的对应表示形式。

3.1. 对象映射基础

本节介绍了 Spring Data 对象映射、对象创建、字段和属性访问、可变性和不变性的基础知识。 请注意,本节仅适用于不使用底层数据存储的对象映射(如 JPA)的 Spring 数据模块。 此外,请务必查阅特定于存储的部分,了解特定于存储的对象映射,例如索引、自定义列或字段名称等。

Spring 数据对象映射的核心职责是创建域对象的实例,并将存储本机数据结构映射到这些实例上。 这意味着我们需要两个基本步骤:

  1. 使用公开的构造函数之一创建实例。
  2. 实例填充以具体化所有公开的属性。

3.1.1. 对象创建

Spring Data 自动尝试检测持久实体的构造函数,以用于具体化该类型的对象。 解析算法的工作原理如下:

  1. 如果有一个静态工厂方法注释,则使用它。@PersistenceCreator
  2. 如果只有一个构造函数,则使用它。
  3. 如果有多个构造函数,并且只有一个被批注,则使用它。@PersistenceCreator
  4. 如果类型是 Java,则使用规范构造函数。Record
  5. 如果存在无参数构造函数,则使用它。 其他构造函数将被忽略。

值解析假定构造函数/工厂方法参数名称与实体的属性名称匹配,即解析将像要填充属性一样执行,包括映射中的所有自定义(不同的数据存储列或字段名称等)。 这还需要类文件中可用的参数名称信息或构造函数上存在的枚举注释。​​@ConstructorProperties​

值解析可以通过使用特定于商店的SpEL表达式使用Spring Framework的值注释来自定义。 有关更多详细信息,请参阅商店特定映射部分。​​@Value​

对象创建内部

为了避免反射的开销,Spring Data 对象创建默认使用运行时生成的工厂类,该工厂类将直接调用域类构造函数。 即对于此示例类型:

class Person 
Person(String firstname, String lastname) …

我们将在运行时创建一个语义等同于此工厂类的工厂类:

class PersonObjectInstantiator implements ObjectInstantiator 

Object newInstance(Object... args)
return new Person((String) args[0], (String) args[1]);

这为我们提供了大约 10% 的性能提升。 要使域类符合此类优化的条件,它需要遵守一组约束:

  • 它不能是私有类
  • 它不能是非静态内部类
  • 它不能是 CGLib 代理类
  • Spring Data 要使用的构造函数不得是私有的

如果这些条件中的任何一个匹配,Spring 数据将通过反射回退到实体实例化。

3.1.2. 财产人口

创建实体的实例后,Spring Data 将填充该类的所有剩余持久属性。 除非已由实体的构造函数填充(即通过其构造函数参数列表使用),否则将首先填充标识符属性以允许解析循环对象引用。 之后,将在实体实例上设置构造函数尚未填充的所有非瞬态属性。 为此,我们使用以下算法:

  1. 如果属性是不可变的,但公开了 amethod(见下文),我们使用该方法创建一个具有新属性值的新实体实例。with…with…
  2. 如果定义了属性访问(即通过 getter 和 setter 的访问),我们将调用 setter 方法。
  3. 如果属性是可变的,我们直接设置字段。
  4. 如果属性是不可变的,我们将使用持久性操作要使用的构造函数(请参阅对象创建)来创建实例的副本。
  5. 默认情况下,我们直接设置字段值。

属性人口内部

与对象构造中的优化类似,我们还使用 Spring 数据运行时生成的访问器类与实体实例进行交互。

class Person 

private final Long id;
private String firstname;
private @AccessType(Type.PROPERTY) String lastname;

Person()
this.id = null;


Person(Long id, String firstname, String lastname)
// Field assignments


Person withId(Long id)
return new Person(id, this.firstname, this.lastame);


void setLastname(String lastname)
this.lastname = lastname;

例 4.生成的属性访问器

class PersonPropertyAccessor implements PersistentPropertyAccessor 

private static final MethodHandle firstname;

private Person person;

public void setProperty(PersistentProperty property, Object value)

String name = property.getName();

if ("firstname".equals(name))
firstname.invoke(person, (String) value);
else if ("id".equals(name))
this.person = person.withId((Long) value);
else if ("lastname".equals(name))
this.person.setLastname((String) value);



PropertyAccessor 保存基础对象的可变实例。这是为了启用其他不可变属性的突变。


默认情况下,Spring 数据使用字段访问来读取和写入属性值。根据字段的可见性规则,用于与字段进行交互。​​private​​​​MethodHandles​


该类公开用于设置标识符的方法,例如,当实例插入数据存储并生成标识符时。调用创建一个新对象。所有后续突变都将发生在新实例中,而之前的突变保持不变。​​withId(…)​​​​withId(…)​​​​Person​


使用属性访问允许直接调用方法,而无需使用。​​MethodHandles​

这为我们提供了大约 25% 的性能提升。 要使域类符合此类优化的条件,它需要遵守一组约束:

  • 类型不得驻留在默认值或包下。java
  • 类型及其构造函数必须是public
  • 内部类的类型必须是。static
  • 使用的 Java 运行时必须允许在原始文件中声明类。Java 9 及更高版本施加了某些限制。ClassLoader

默认情况下,Spring Data 尝试使用生成的属性访问器,如果检测到限制,则回退到基于反射的属性访问器。

让我们看一下以下实体:

例 5.示例实体

class Person 

private final @Id Long id;
private final String firstname, lastname;
private final LocalDate birthday;
private final int age;

private String comment;
private @AccessType(Type.PROPERTY) String remarks;

static Person of(String firstname, String lastname, LocalDate birthday)

return new Person(null, firstname, lastname, birthday,
Period.between(birthday, LocalDate.now()).getYears());


Person(Long id, String firstname, String lastname, LocalDate birthday, int age)

this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.birthday = birthday;
this.age = age;


Person withId(Long id)
return new Person(id, this.firstname, this.lastname, this.birthday, this.age);


void setRemarks(String remarks)
this.remarks = remarks;

标识符属性是最终的,但在构造函数中设置为。 该类公开用于设置标识符的方法,例如,当实例插入数据存储并生成标识符时。 创建新实例时,原始实例保持不变。 相同的模式通常应用于存储管理的其他属性,但可能必须更改持久性操作。 wither 方法是可选的,因为持久性构造函数(参见 6)实际上是一个复制构造函数,设置属性将转换为创建应用了新标识符值的新实例。​​null​​​​withId(…)​​​​Person​

和属性是普通的不可变属性,可能通过 getter 公开。​​firstname​​​​lastname​

属性是不可变的,但派生自属性。 按照所示的设计,数据库值将胜过默认值,因为 Spring Data 使用唯一声明的构造函数。 即使意图是首选计算,重要的是此构造函数也采用 as 参数(可能忽略它),否则属性填充步骤将尝试设置 age 字段并失败,因为它是不可变的并且不存在任何方法。​​age​​​​birthday​​​​age​​​​with…​

属性是可变的,通过直接设置其字段来填充。​​comment​

属性是可变的,并通过直接设置 thefield 或通过调用 setter 方法来填充​​remarks​​​​comment​

该类公开用于创建对象的工厂方法和构造函数。 这里的核心思想是使用工厂方法而不是其他构造函数,以避免通过构造函数消除歧义的需要。 相反,属性的默认值在工厂方法中处理。 如果您希望 Spring Data 使用工厂方法进行对象实例化,请使用 注释。​​@PersistenceCreator​​​​@PersistenceCreator​

3.1.3. 一般建议

  • 尝试坚持使用不可变对象 - 不可变对象很容易创建,因为具体化对象只需调用其构造函数即可。 此外,这可以避免域对象充斥着允许客户端代码操作对象状态的 setter 方法。 如果需要这些,最好使它们受到包保护,以便只能由有限数量的共存类型调用它们。 仅构造函数具体化比属性填充快 30%。
  • 提供全参数构造函数 — 即使不能或不想将实体建模为不可变值,提供将实体的所有属性(包括可变属性)作为参数的构造函数仍然有价值,因为这允许对象映射跳过属性填充以获得最佳性能。
  • 使用工厂方法而不是重载的构造函数来避免​​@PersistenceCreator​​ — 使用最佳性能所需的全参数构造函数,我们通常希望公开更多特定于应用程序用例的构造函数,这些构造函数省略了自动生成的标识符等内容。 这是一种既定模式,而是使用静态工厂方法来公开 all-args 构造函数的这些变体。
  • 确保遵守允许使用生成的实例化器和属性访问器类的约束
  • 对于要生成的标识符,仍将最终字段与全参数持久性构造函数(首选)或​​with​​...方法
  • 使用 Lombok 避免样板代码 — 由于持久性操作通常需要构造函数获取所有参数,因此它们的声明变成了对字段分配的繁琐重复,而使用 Lombok 可以最好地避免这些参数。@AllArgsConstructor
覆盖属性

Java允许灵活设计域类,其中子类可以定义已在其超类中声明具有相同名称的属性。 请考虑以下示例:

public class SuperType 

private CharSequence field;

public SuperType(CharSequence field)
this.field = field;


public CharSequence getField()
return this.field;


public void setField(CharSequence field)
this.field = field;



public class SubType extends SuperType

private String field;

public SubType(String field)
super(field);
this.field = field;


@Override
public String getField()
return this.field;


public void setField(String field)
this.field = field;

// optional
super.setField(field);

这两个类都使用可赋值类型定义。 根据类设计,使用构造函数可能是唯一的默认设置方法。 或者,调用二传手可以设置。 所有这些机制在某种程度上都会产生冲突,因为属性共享相同的名称,但可能表示两个不同的值。 如果类型不可分配,则 Spring Data 将跳过超类型属性。 也就是说,重写属性的类型必须可分配给其超类型属性类型才能注册为重写,否则超类型属性被视为暂时性属性。 我们通常建议使用不同的属性名称。​​field​​​​SubType​​​​SuperType.field​​​​SuperType.field​​​​super.setField(…)​​​​field​​​​SuperType​

Spring 数据模块通常支持保存不同值的被覆盖属性。 从编程模型的角度来看,需要考虑以下几点:

  1. 应保留哪个属性(默认为所有声明的属性)? 您可以通过用这些属性批注来排除属性。@Transient
  2. 如何表示数据存储中的属性? 对不同的值使用相同的字段/列名称通常会导致数据损坏,因此应使用显式字段/列名称至少对其中一个属性进行批注。
  3. 不能使用,因为如果不对 setter 实现进行任何进一步的假设,通常无法设置超级属性。@AccessType(PROPERTY)

3.1.4. Kotlin 支持

Spring Data 调整了 Kotlin 的细节,以允许对象创建和更改。

Kotlin 对象创建

Kotlin 类支持实例化,默认情况下所有类都是不可变的,并且需要显式属性声明来定义可变属性。

Spring Data 自动尝试检测持久实体的构造函数,以用于具体化该类型的对象。 解析算法的工作原理如下:

  1. 如果存在带有注释的构造函数,则使用它。@PersistenceCreator
  2. 如果类型是Kotlin 数据 cass,则使用主构造函数。
  3. 如果有一个静态工厂方法注释,则使用它。@PersistenceCreator
  4. 如果只有一个构造函数,则使用它。
  5. 如果有多个构造函数,并且只有一个被批注,则使用它。@PersistenceCreator
  6. 如果类型是 Java,则使用规范构造函数。Record
  7. 如果存在无参数构造函数,则使用它。 其他构造函数将被忽略。

请考虑以下类:​​data​​​​Person​

data class Person(val id: String, val name: String)

上面的类编译为具有显式构造函数的典型类。我们可以通过添加另一个构造函数来自定义此类并对其进行注释以指示构造函数首选项:​​@PersistenceCreator​

data class Person(var id: String, val name: String) 

@PersistenceCreator
constructor(id: String) : this(id, "unknown")

Kotlin 通过允许在未提供参数时使用默认值来支持参数可选性。 当 Spring Data 检测到参数默认值的构造函数时,如果数据存储不提供值(或只是返回),则这些参数将保留为不存在,以便 Kotlin 可以应用参数默认值。请考虑以下应用参数默认值的类​​null​​​​name​

data class Person(var id: String, val name: String = "unknown")

每次参数不是结果的一部分或其值是时,则默认为。​​name​​​​null​​​​name​​​​unknown​

Kotlin 数据类的属性填充

在 Kotlin 中,默认情况下所有类都是不可变的,并且需要显式属性声明来定义可变属性。 请考虑以下类:​​data​​​​Person​

data class Person(val id: String, val name: String)

此类实际上是不可变的。 它允许在 Kotlin 生成方法时创建新实例,该方法创建新的对象实例,从现有对象复制所有属性值,并将作为参数提供的属性值应用于该方法。​​copy(…)​

Kotlin 覆盖属性

Kotlin 允许声明属性覆盖以更改子类中的属性。

open class SuperType(open var field: Int)

class SubType(override var field: Int = 1) :
SuperType(field)

这种安排呈现两个具有名称的属性。 Kotlin 为每个类中的每个属性生成属性访问器(getter 和 setter)。 实际上,代码如下所示:​​field​

public class SuperType 

private int field;

public SuperType(int field)
this.field = field;


public int getField()
return this.field;


public void setField(int field)
this.field = field;



public final class SubType extends SuperType

private int field;

public SubType(int field)
super(field);
this.field = field;


public int getField()
return this.field;


public void setField(int field)
this.field = field;

吉特和二传手仅发病,不发病。 在这种安排中,使用构造函数是唯一要设置的默认方法。 可以添加方法toto setvia,但不属于支持的约定。 属性重写在某种程度上会产生冲突,因为属性共享相同的名称,但可能表示两个不同的值。 我们通常建议使用不同的属性名称。​​SubType​​​​SubType.field​​​​SuperType.field​​​​SuperType.field​​​​SubType​​​​SuperType.field​​​​this.SuperType.field = …​

Spring 数据模块通常支持保存不同值的被覆盖属性。 从编程模型的角度来看,需要考虑以下几点:

  1. 应保留哪个属性(默认为所有声明的属性)? 您可以通过用这些属性批注来排除属性。@Transient
  2. 如何表示数据存储中的属性? 对不同的值使用相同的字段/列名称通常会导致数据损坏,因此应使用显式字段/列名称至少对其中一个属性进行批

    以上是关于Spring Data(数据) Couchbase的主要内容,如果未能解决你的问题,请参考以下文章

    是否可以将Spring Data Couchbase映射到外部文档(来自依赖)?

    无法执行 Spring-data Couchbase 查询

    Spring Data vs Couchbase SDK [关闭]

    Spring认证中国教育管理中心-Spring Data Couchbase教程六

    Spring认证中国教育管理中心-Spring Data Couchbase教程九

    使用 Couchbase 作为持久性的 Apache Ignite