一、简介

在本关于 Spring Data 的教程中,我们将讨论如何使用 Spring Data 存储库和模板抽象为 Couchbase 文档设置持久层,以及准备 Couchbase 以使用视图和/或索引支持这些抽象所需的步骤。

2.Maven依赖

首先,我们将以下 Maven 依赖项添加到 pom.xml 文件中:

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

请注意,通过包含此依赖项,我们会自动获取本机 Couchbase SDK 的兼容版本,因此我们不需要显式包含它。

为了添加对 JSR-303 bean 验证的支持,我们还包含以下依赖项:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.2.4.Final</version>
</dependency>

Spring Data Couchbase 通过传统的 Date 和 Calendar 类以及 Joda Time 库支持日期和时间持久化,我们包括如下:

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.2</version>
</dependency>

3. 配置

接下来,我们需要通过指定 Couchbase 集群的一个或多个节点以及用于存储文档的存储桶的名称和密码来配置 Couchbase 环境。

3.1. Java配置

对于 Java 类配置,我们只需扩展 AbstractCouchbaseConfiguration 类即可:

@Configuration
@EnableCouchbaseRepositories(basePackages={"com.baeldung.spring.data.couchbase"})
public class MyCouchbaseConfig extends AbstractCouchbaseConfiguration {

    @Override
    protected List<String> getBootstrapHosts() {
        return Arrays.asList("localhost");
    }

    @Override
    protected String getBucketName() {
        return "baeldung";
    }

    @Override
    protected String getBucketPassword() {
        return "";
    }
}

如果您的项目需要对 Couchbase 环境进行更多自定义,您可以通过重写 getEnvironment() 方法来提供:

@Override
protected CouchbaseEnvironment getEnvironment() {
   ...
}

3.2. XML配置

以下是 XML 中的等效配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/data/couchbase
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/couchbase
    http://www.springframework.org/schema/data/couchbase/spring-couchbase.xsd">

    <couchbase:cluster>
        <couchbase:node>localhost</couchbase:node>
    </couchbase:cluster>

    <couchbase:clusterInfo login="baeldung" password="" />

    <couchbase:bucket bucketName="baeldung" bucketPassword=""/>

    <couchbase:repositories base-package="com.baeldung.spring.data.couchbase"/>
</beans:beans>

注意:“ clusterInfo ”节点接受集群凭据或存储桶凭据,并且是 必需的 ,以便库可以确定您的 Couchbase 集群是否支持 N1QL(NoSQL 数据库的 SQL 超集,在 Couchbase 4.0 及更高版本中可用)。

如果您的项目需要自定义 Couchbase 环境,您可以使用 couchbase:env/ 标签提供一个环境。

4. 数据模型

让我们创建一个表示要持久保存的 JSON 文档的实体类。我们首先用 @Document注释该类, 然后用 @Id 注释一个 String 字段来表示Couchbase文档键。

您可以使用 Spring Data 中的 @Id 注释,也可以使用本机 Couchbase SDK 中的 @Id 注释。请注意,如果您在同一类中的两个不同字段上使用两个 @Id 注释,则使用 Spring Data @Id 注释注释的字段将优先并用作文档键。

为了表示 JSON 文档的属性,我们添加用 @Field 注释的私有成员变量。我们使用 @NotNull 注释来标记某些字段为必填项:

@Document
public class Person {
    @Id
    private String id;
    
    @Field
    @NotNull
    private String firstName;
    
    @Field
    @NotNull
    private String lastName;
    
    @Field
    @NotNull
    private DateTime created;
    
    @Field
    private DateTime updated;
    
    // standard getters and setters
}

请注意,用 @Id 注释的属性仅代表文档键,不一定是存储的 JSON 文档的一部分,除非它也用 @Field 注释,如下所示:

@Id
@Field
private String id;

如果您想要为实体类中的字段命名与 JSON 文档中存储的字段不同,只需限定其 @Field 注释,如下例所示:

@Field("fname")
private String firstName;

以下示例展示了持久保存的 Person 文档的外观:

{
    "firstName": "John",
    "lastName": "Smith",
    "created": 1457193705667
    "_class": "com.baeldung.spring.data.couchbase.model.Person"
}

请注意,Spring Data 会自动向每个文档添加一个包含实体的完整类名的属性。默认情况下,此属性名为 “_class” ,尽管您可以通过覆盖 typeKey() 方法来覆盖 Couchbase 配置类中的该属性。

例如,如果您想指定一个名为 “dataType” 的字段来保存类名称,您可以将其添加到您的 Couchbase 配置类中:

@Override
public String typeKey() {
    return "dataType";
}

覆盖 typeKey() 的 另一个常见原因是,如果您使用的 Couchbase Mobile 版本不支持以下划线前缀的字段。在这种情况下,您可以像前面的示例一样选择自己的备用类型字段,也可以使用 Spring 提供的备用类型:

@Override
public String typeKey() {
    // use "javaClass" instead of "_class"
    return MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE;
}

5.Couchbase 存储库

Spring Data Couchbase 提供与其他 Spring Data 模块(例如 JPA)相同的内置查询和派生查询机制。

我们通过扩展 CrudRepository<String,Person> 并添加可派生的查询方法来声明 Person 类的存储库接口:

public interface PersonRepository extends CrudRepository<Person, String> {
    List<Person> findByFirstName(String firstName);
}

6. 通过索引支持 N1QL

如果使用 Couchbase 4.0 或更高版本,则默认情况下,将使用 N1QL 引擎处理自定义查询(除非其相应的存储库方法使用 @View 进行注释以指示使用支持视图,如下一节所述)。

要添加对 N1QL 的支持,您必须在存储桶上创建主索引。您可以使用 cbq 命令行查询处理器(请参阅 Couchbase 文档了解如何针对您的环境启动 cbq 工具)并发出以下命令来创建索引:

CREATE PRIMARY INDEX ON baeldung USING GSI;

在上面的命令中, GSI 代表 全局二级索引 ,它是一种特别适合优化即席N1QL查询以支持OLTP系统的索引类型,如果没有另外指定,它是默认索引类型。

与基于视图的索引不同,GSI 索引不会在集群中的所有索引节点之间自动复制,因此,如果您的集群包含多个索引节点,则需要在集群中的每个节点上创建每个 GSI 索引,并且必须提供每个节点上有不同的索引名称。

您还可以创建一个或多个二级索引。当您这样做时,Couchbase 将根据需要使用它们以优化其查询处理。

例如,要在 firstName 字段上添加索引,请在 cbq 工具中发出以下命令:

CREATE INDEX idx_firstName ON baeldung(firstName) USING GSI;

7.支持观点

对于每个存储库接口,您需要在目标存储桶中创建一个 Couchbase 设计文档和一个或多个视图。设计文档名称必须是实体类名称的 小驼峰 版本(例如 “person” )。

无论您运行哪个版本的 Couchbase Server,都必须创建一个名为 “all” 的支持视图来支持内置的“ findAll” 存储库方法。这是我们的 Person 类的 “全部” 视图的映射函数:

function (doc, meta) {
    if(doc._class == "com.baeldung.spring.data.couchbase.model.Person") {
        emit(meta.id, null);
    }
}

使用 4.0 之前的 Couchbase 版本时,每个自定义存储库方法都必须有一个支持视图(在 4.0 或更高版本中,支持视图的使用是可选的)。

视图支持的自定义方法必须使用 @View 进行注释,如下例所示:

@View
List<Person> findByFirstName(String firstName);

支持视图的默认命名约定是使用“ find” 关键字后面的方法名称部分的 小驼峰 版本(例如 “byFirstName” )。

以下是为 “byFirstName” 视图编写地图函数的方法:

function (doc, meta) {
    if(doc._class == "com.baeldung.spring.data.couchbase.model.Person"
      && doc.firstName) {
        emit(doc.firstName, null);
    }
}

您可以覆盖此命名约定并使用您自己的视图名称,方法是使用相应支持视图的名称限定每个 @View 注释。例如:

@View("myCustomView")
List<Person> findByFirstName(String lastName);

8.服务层

对于我们的服务层,我们定义一个接口和两个实现:一个使用 Spring Data 存储库抽象,另一个使用 Spring Data 模板抽象。这是我们的 PersonService 接口:

public interface PersonService {
    Person findOne(String id);
    List<Person> findAll();
    List<Person> findByFirstName(String firstName);
    
    void create(Person person);
    void update(Person person);
    void delete(Person person);
}

8.1.存储库服务

这是使用我们上面定义的存储库的实现:

@Service
@Qualifier("PersonRepositoryService")
public class PersonRepositoryService implements PersonService {
    
    @Autowired
    private PersonRepository repo; 

    public Person findOne(String id) {
        return repo.findOne(id);
    }

    public List<Person> findAll() {
        List<Person> people = new ArrayList<Person>();
        Iterator<Person> it = repo.findAll().iterator();
        while(it.hasNext()) {
            people.add(it.next());
        }
        return people;
    }

    public List<Person> findByFirstName(String firstName) {
        return repo.findByFirstName(firstName);
    }

    public void create(Person person) {
        person.setCreated(DateTime.now());
        repo.save(person);
    }

    public void update(Person person) {
        person.setUpdated(DateTime.now());
        repo.save(person);
    }

    public void delete(Person person) {
        repo.delete(person);
    }
}

8.2.模板服务

对于基于模板的实现,我们必须创建上面第 7 节中列出的支持视图。 CouchbaseTemplate 对象在我们的 Spring 上下文中可用,并且可以注入到服务类中。

这是使用模板抽象的实现:

@Service
@Qualifier("PersonTemplateService")
public class PersonTemplateService implements PersonService {
    private static final String DESIGN_DOC = "person";
    @Autowired
    private CouchbaseTemplate template;
    
    public Person findOne(String id) {
       return template.findById(id, Person.class);
    }

    public List<Person> findAll() {
        return template.findByView(ViewQuery.from(DESIGN_DOC, "all"), Person.class);
    }

    public List<Person> findByFirstName(String firstName) {
        return template.findByView(ViewQuery.from(DESIGN_DOC, "byFirstName"), Person.class);
    }

    public void create(Person person) {
        person.setCreated(DateTime.now());
        template.insert(person);
    }

    public void update(Person person) {
        person.setUpdated(DateTime.now());
        template.update(person);
    }

    public void delete(Person person) {
        template.remove(person);
    }
}

9. 结论

我们展示了如何配置项目以使用 Spring Data Couchbase 模块以及如何编写简单的实体类及其存储库接口。我们编写了一个简单的服务接口,并提供了一个使用存储库的实现和另一个使用 Spring Data 模板 API 的实现。

您可以在GitHub 项目中查看本教程的完整源代码。

有关更多信息,请访问Spring Data Couchbase项目站点。