Contents

Eclipse JNoSQL 简介

1. 概述

Eclipse JNoSQL 是一组 API 和实现,可简化 Java 应用程序与 NoSQL 数据库的交互

在本文中,我们将学习如何设置和配置 JNoSQL 以与 NoSQL 数据库交互。我们将同时使用通信和映射层。

2. Eclipse JNoSQL 通信层

从技术上讲,通信层由两个模块组成:Diana API 和驱动程序。

虽然 API 定义了 NoSQL 数据库类型的抽象,但驱动程序提供了大多数已知数据库的实现

我们可以将其与关系数据库中的 JDBC API 和 JDBC 驱动程序进行比较。

2.1. Eclipse JNoSQL Diana API

简单来说,NoSQL 数据库有四种基本类型:Key-Value、Column、Document 和 Graph。

Eclipse JNoSQL Diana API 定义了三个模块:

  1. diana-key-value
  2. diana-column
  3. diana-document

API 未涵盖 NoSQL 图类型,因为 Apache ThinkerPop 已经涵盖了它。

API 基于核心模块 diana-core,并定义了对常见概念的抽象,例如 Configuration、Factory、Manager、Entity 和 Value。

要使用 API,我们需要为我们的 NoSQL 数据库类型提供相应模块的依赖关系。

因此,对于面向文档的数据库,我们需要 diana-document依赖项:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>diana-document</artifactId>
    <version>0.0.6</version>
</dependency>

同样,如果工作的 NoSQL 数据库是面向键值的,我们应该使用diana-key-value模块:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>diana-key-value</artifactId>
    <version>0.0.6</version>
</dependency>

最后是diana-column模块,如果它是面向列的:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>diana-column</artifactId>
    <version>0.0.6</version>
</dependency>

最新版本可以在 Maven Central 上找到。

2.2. Eclipse JNoSQL diana驱动程序

驱动程序是一组用于最常见 NoSQL 数据库的 API 实现

每个 NoSQL 数据库都有一个实现。如果数据库是多模型的,驱动程序应该实现所有支持的 API

例如,couchbase-driver同时实现了diana-documentdiana-key-value,因为 Couchbase 既面向文档又面向键值。

与关系数据库不同,驱动程序通常由数据库供应商提供,这里的驱动程序由 Eclipse JNoSQL 提供。在大多数情况下,此驱动程序是官方供应商库的包装器。

要开始使用驱动程序,我们应该包含 API 和所选 NoSQL 数据库的相应实现。

例如,对于 MongoDB,我们需要包含以下依赖项:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>diana-document</artifactId>
    <version>0.0.6</version>
</dependency>
<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>0.0.6</version>
</dependency>

使用驱动程序的过程很简单。

首先,我们需要一个Configuration bean。通过从类路径或硬编码值读取配置文件, Configuration能够创建Factory。然后我们使用它来创建一个Manager

最后,Manager负责在 NoSQL 数据库中推送和检索实体

在接下来的小节中,我们将为每个 NoSQL 数据库类型解释这个过程。

2.3. 使用面向文档的数据库

在此示例中,我们将使用嵌入式 MongoDB,因为它很容易上手并且不需要安装。它是面向文档的,以下说明适用于任何其他面向文档的 NoSQL 数据库。

一开始,我们应该提供应用程序与数据库正确交互所需的所有必要设置。在最基本的形式中,我们应该提供 MongoDB 运行实例的HostPort

我们可以在类路径上的mongodb-driver.properties中提供这些设置:

#Define Host and Port
mongodb-server-host-1=localhost:27017

或作为硬编码值:

Map<String, Object> map = new HashMap<>();
map.put("mongodb-server-host-1", "localhost:27017");

接下来,我们为文档类型创建Configurationbean:

DocumentConfiguration configuration = new MongoDBDocumentConfiguration();

从这个Configuration bean,我们可以创建一个ManagerFactory

DocumentCollectionManagerFactory managerFactory = configuration.get();

隐式地,Configuration bean 的get() 方法使用属性文件中的设置。我们也可以从硬编码的值中获取这个工厂:

DocumentCollectionManagerFactory managerFactory 
  = configuration.get(Settings.of(map));

ManagerFactory有一个简单的方法get(),它将数据库名称作为参数,并创建Manager

DocumentCollectionManager manager = managerFactory.get("my-db");

最后,我们准备好了。Manager 提供了通过DocumentEntity与底层 NoSQL 数据库交互的 所有必要方法。 因此,例如,我们可以插入一个文档:

DocumentEntity documentEntity = DocumentEntity.of("books");
documentEntity.add(Document.of("_id", "100"));
documentEntity.add(Document.of("name", "JNoSQL in Action"));
documentEntity.add(Document.of("pages", "620"));
DocumentEntity saved = manager.insert(documentEntity);

我们还可以搜索文档:

DocumentQuery query = select().from("books").where("_id").eq(100).build();
List<DocumentEntity> entities = manager.select(query);

并且以类似的方式,我们可以更新现有文档:

saved.add(Document.of("author", "blogdemo"));
DocumentEntity updated = manager.update(saved);

最后,我们可以删除存储的文档:

DocumentDeleteQuery deleteQuery = delete().from("books").where("_id").eq("100").build();
manager.delete(deleteQuery);

要运行示例,我们只需要访问jnosql-diana模块并运行DocumentApp应用程序。

我们应该在控制台中看到输出:

DefaultDocumentEntity{documents={pages=620, name=JNoSQL in Action, _id=100}, name='books'}
DefaultDocumentEntity{documents={pages=620, author=blogdemo, name=JNoSQL in Action, _id=100}, name='books'}
[]

2.4. 使用面向列的数据库

出于本节的目的,我们将使用 Cassandra 数据库的嵌入式版本,因此无需安装。

使用面向列的数据库的过程非常相似。首先,我们在 pom 中添加 Cassandra 驱动和列 API:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>diana-column</artifactId>
    <version>0.0.6</version>
</dependency>
<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>cassandra-driver</artifactId>
    <version>0.0.6</version>
</dependency>

接下来,我们需要在类路径上的配置文件diana-cassandra.properties中指定的配置设置 。或者,我们也可以使用硬编码的配置值。

然后,使用类似的方法,我们将创建一个ColumnFamilyManager并开始操作 ColumnEntity

ColumnConfiguration configuration = new CassandraConfiguration();
ColumnFamilyManagerFactory managerFactory = configuration.get();
ColumnFamilyManager entityManager = managerFactory.get("my-keySpace");

所以要创建一个新实体,让我们调用*insert()*方法:

ColumnEntity columnEntity = ColumnEntity.of("books");
Column key = Columns.of("id", 10L);
Column name = Columns.of("name", "JNoSQL in Action");
columnEntity.add(key);
columnEntity.add(name);
ColumnEntity saved = entityManager.insert(columnEntity);

要运行示例并在控制台中查看输出,请运行ColumnFamilyApp应用程序。

2.5. 使用面向键值的数据库

在本节中,我们将使用 Hazelcast。Hazelcast 是一个面向键值的 NoSQL 数据库。有关 Hazelcast 数据库的更多信息,您可以查看此链接

使用面向键值的类型的过程也类似。我们首先将这些依赖项添加到 pom 中:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>diana-key-value</artifactId>
    <version>0.0.6</version>
</dependency>
<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>hazelcast-driver</artifactId>
    <version>0.0.6</version>
</dependency>

然后我们需要提供配置设置。接下来,我们可以获取一个BucketManager,然后对 KeyValueEntity 进行操作:

KeyValueConfiguration configuration = new HazelcastKeyValueConfiguration();
BucketManagerFactory managerFactory = configuration.get();
BucketManager entityManager = managerFactory.getBucketManager("books");

假设我们要保存以下Book模型:

public class Book implements Serializable {
    private String isbn;
    private String name;
    private String author;
    private int pages;
    // standard constructor
    // standard getters and setters
}

所以我们创建了一个Book实例,然后我们通过调用*put()*方法来保存它;

Book book = new Book(
  "12345", "JNoSQL in Action", 
  "blogdemo", 420);
KeyValueEntity keyValueEntity = KeyValueEntity.of(
  book.getIsbn(), book);
entityManager.put(keyValueEntity);

然后检索保存的Book实例:

Optional<Value> optionalValue = manager.get("12345");
Value value = optionalValue.get(); // or any other adequate Optional handling
Book savedBook = value.get(Book.class);

要运行示例并在控制台中查看输出,请运行KeyValueApp应用程序。

3. Eclipse JNoSQL 映射层

映射层 Artemis API 是一组 API,可帮助将 java 带注释的对象映射到 NoSQL 数据库。它基于 Diana API 和 CDI(上下文和依赖注入)。

我们可以将此 API 视为 JPA 或类似 NoSQL 世界的 ORM。这一层还为每种 NoSQL 类型提供了一个 API,并为常用功能提供了一个核心 API。

在本节中,我们将使用 MongoDB 面向文档的数据库。

3.1. 所需的依赖项

要在应用程序中启用 Artemis,我们需要添加*artemis-configuration * 依赖项。由于 MongoDB 是面向文档的, 因此还需要依赖artemis-document

对于其他类型的 NoSQL 数据库,我们将使用artemis-column、artemis-key-valueartemis-graph

还需要 MongoDB 的 Diana 驱动程序:

<dependency>
    <groupId>org.jnosql.artemis</groupId>
    <artifactId>artemis-configuration</artifactId>
    <version>0.0.6</version>
</dependency>
<dependency>
    <groupId>org.jnosql.artemis</groupId>
    <artifactId>artemis-document</artifactId>
    <version>0.0.6</version>
</dependency>
<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>0.0.6</version>
</dependency>

Artemis 是基于 CDI 的,所以我们还需要提供这个 Maven 依赖:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>8.0</version>
    <scope>provided</scope>
</dependency>

3.2. 文档配置文件

配置是给定数据库的一组属性,让我们在代码之外提供设置。默认情况下,我们需要 在 META-INF 资源下提供jnosql.json文件。

这是配置文件的示例:

[
    {
        "description": "The mongodb document configuration",
        "name": "document",
        "provider": "org.jnosql.diana.mongodb.document.MongoDBDocumentConfiguration",
        "settings": {
            "mongodb-server-host-1":"localhost:27019"
        }
    }
]

我们需要通过在 ConfigurationUnit 中设置 name 属性来指定上面的配置name。如果配置在不同的文件中,可以使用fileName属性来指定。

给定这个配置,我们创建一个工厂:

@Inject
@ConfigurationUnit(name = "document")
private DocumentCollectionManagerFactory<MongoDBDocumentCollectionManager> managerFactory;

从这个工厂,我们可以创建一个DocumentCollectionManager

@Produces
public MongoDBDocumentCollectionManager getEntityManager() {
    return managerFactory.get("todos");
}

DocumentCollectionManager是一个启用 CDI的bean,它用于Template 和 Repository中。

3.3. 映射

映射是一个注释驱动的过程,通过该过程,Entity模型被转换为 Diana EntityValue

让我们从定义一个Todo模型开始:

@Entity
public class Todo implements Serializable {
    @Id("id")
    public long id;
    @Column
    public String name;
    @Column
    public String description;
    // standard constructor
    // standard getters and setters
}

如上所示,我们有基本的映射注解:  @Entity、@Id和*@Column*。

现在要操作这个模型,我们需要一个Template类或一个Repository接口。

3.4. 使用模板

模板是实体模型和 Diana API 之间的桥梁。对于面向文档的数据库,我们首先注入DocumentTemplate bean:

@Inject
DocumentTemplate documentTemplate;

然后,我们可以操作Todo实体。例如,我们可以创建一个Todo

public Todo add(Todo todo) {
    return documentTemplate.insert(todo);
}

或者我们可以通过id检索Todo

public Todo get(String id) {
    Optional<Todo> todo = documentTemplate
      .find(Todo.class, id);
    return todo.get(); // or any other proper Optional handling
}

要选择所有实体,我们构建一个DocumentQuery,然后调用*select()*方法:

public List<Todo> getAll() {
    DocumentQuery query = select().from("Todo").build();
    return documentTemplate.select(query);
}

最后我们可以通过id删除一个Todo 实体:

public void delete(String id) {
    documentTemplate.delete(Todo.class, id);
}

3.5. 使用存储库

除了Template类,我们还可以通过Repository接口管理实体,该接口具有创建、更新、删除和检索信息的方法。

要使用Repository接口,我们只需提供Repository 的子接口:

public interface TodoRepository extends Repository<Todo, String> {
    List<Todo> findByName(String name);
    List<Todo> findAll();
}

通过以下方法和参数命名约定,此接口的实现在运行时作为 CDI bean 提供。

在此示例中,所有具有匹配名称的Todo实体 都由*findByName()*方法检索。

我们现在可以使用它:

@Inject
TodoRepository todoRepository;

Database限定符让我们可以在同一个应用程序中使用多个 NoSQL 数据库。它带有两个属性,类型和提供者。

如果数据库是多模型的,那么我们需要指定我们正在使用的模型:

@Inject
@Database(value = DatabaseType.DOCUMENT)
TodoRepository todoRepository;

另外,如果我们有多个相同模型的数据库,我们需要指定提供者:

@Inject
@Database(value = DatabaseType.DOCUMENT, provider="org.jnosql.diana.mongodb.document.MongoDBDocumentConfiguration")
TodoRepository todoRepository;

要运行示例,只需访问 jnosql-artemis 模块并调用以下命令:

mvn package liberty:run

由于 liberty-maven-plugin ,此命令构建、部署和启动Open Liberty 服务器。

3.6. 测试应用程序

由于应用程序公开了一个 REST 端点,我们可以使用任何 REST 客户端进行测试。这里我们使用了 curl 工具。

所以要保存一个 Todo 类:

curl -d '{"id":"120", "name":"task120", "description":"Description 120"}' -H "Content-Type: application/json" -X POST http://localhost:9080/jnosql-artemis/todos

并获得所有待办事项:

curl -H "Accept: application/json" -X GET http://localhost:9080/jnosql-artemis/todos

或者只获取一个 Todo:

curl -H "Accept: application/json" -X GET http://localhost:9080/jnosql-artemis/todos/120