Introduction to FaunaDB with Spring – 用Spring介绍FaunaDB

最后修改: 2022年 1月 9日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this article, we’re going to explore the Fauna distributed database. We’ll see what features it brings to our applications, what we can do with it, and how to interact with it.

在本文中,我们将探讨Fauna 分布式数据库我们将看到它为我们的应用程序带来了哪些功能,我们可以用它做什么,以及如何与它互动。

2. What Is Fauna?

2.什么是动物群?

Fauna is a multi-protocol, multi-model, multi-tenant, distributed, transactional Database as a Service (DBaaS) offering. This sounds complicated, so let’s break it down a bit.

Fauna是一个多协议、多模型、多租户、分布式、交易型数据库即服务(DBaaS)产品。这听起来很复杂,所以让我们把它分解一下。

2.1. Database as a Service

2.1.数据库即服务

A “Database as a Service” means that the database is hosted by a cloud provider, who takes care of all of the infrastructure and maintenance so that we’re left to deal only with our domain-specific details — collections, indices, queries, etc. This helps to remove a lot of the complexity of managing such a system while still benefiting from its features.

“数据库即服务 “意味着数据库由云提供商托管,该提供商负责所有的基础设施和维护,这样我们就只需要处理我们特定领域的细节问题–集合、索引、查询等等。这有助于消除管理这种系统的许多复杂性,同时还能从其功能中获益。

2.2. Distributed Transactional Database

2.2.分布式事务性数据库

Being distributed means that the database is running across multiple servers. This helps to make it more efficient and more fault-tolerant at the same time. If one server fails, then the entire database is still able to continue working correctly.

分布式意味着数据库在多个服务器上运行。这有助于使其更有效率,同时也更具有容错性。如果一个服务器出现故障,那么整个数据库仍然能够继续正常工作。

Being transactional means that the database offers strong guarantees about the validity of the data. Data updates performed within a single transaction either succeed or fail as a whole, without the risk of leaving the data in a partial state.

成为交易型数据库意味着数据库对数据的有效性提供了强有力的保证。在单个交易中执行的数据更新要么成功,要么整体失败,而没有让数据处于部分状态的风险。

As a further measure, Fauna offers isolation levels that will ensure that the result of playing multiple transactions across multiple distributed nodes is always correct. This is an important consideration for distributed databases — otherwise, it is possible that different transactions can be played differently on the different nodes and end up with different results.

作为进一步的措施,Fauna提供了隔离级别,将确保在多个分布式节点上播放多个事务的结果总是正确的。这是分布式数据库的一个重要考虑因素–否则,不同的事务有可能在不同的节点上以不同的方式播放,最终得到不同的结果。

For example, let’s consider the following transactions applying to the same record:

例如,让我们考虑以下适用于同一记录的交易。

  1. Set the value to “15”
  2. Increment the value by “3”

If they’re played in the order shown, the end result will be “18”. However, if they’re played in the opposite order, the end result will be “15”. This is even more confusing if the result is different on different nodes in the same system, as it means our data will be inconsistent across the nodes.

如果它们按所示顺序出牌,最终结果将是 “18”。然而,如果它们以相反的顺序播放,最终结果将是 “15”。如果同一系统的不同节点上的结果不同,这就更令人困惑了,因为这意味着我们的数据在不同的节点上是不一致的。

2.3. Multi-Model Database

2.3.多模型数据库

A multi-model database means that it allows us to model different types of data in different ways, all within the same database engine and accessible from the same connections.

多模型数据库意味着它允许我们以不同的方式对不同类型的数据进行建模,所有这些都在同一个数据库引擎中,并可从相同的连接中访问。

Internally, Fauna is a document database. This means that it stores each record as a structured document, with an arbitrary shape represented in JSON. This allows Fauna to act as a key-value store — the document simply has one field, value — or as a tabular store — the document has as many fields as are needed but they’re all flat. However, we can also store more complex documents, with nested fields, arrays, and so on:

在内部,Fauna是一个文档数据库。这意味着它将每条记录作为一个结构化的文档来存储,并以JSON的形式任意表示。这使得Fauna可以作为一个键值存储–文档只有一个字段,value–或者作为一个表格存储–文档有多少字段就有多少字段,但它们都是平面的。然而,我们也可以存储更复杂的文档,有嵌套字段、数组等等。

// Key-Value document
{
  "value": "Baeldung"
}

// Tabular document
{
  "name": "Baeldung",
  "url": "https://www.baeldung.com/"
}

// Structured document
{
  "name": "Baeldung",
  "sites": [
    {
      "id": "cs",
      "name": "Computer Science",
      "url": "https://www.baeldung.com/cs"
    },
    {
      "id": "linux",
      "name": "Linux",
      "url": "https://www.baeldung.com/linux"
    },
    {
      "id": "scala",
      "name": "Scala",
      "url": "https://www.baeldung.com/scala"
    },
    {
      "id": "kotlin",
      "name": "Kotlin",
      "url": "https://www.baeldung.com/kotlin"
    },
  ]
}

On top of this, we also have access to some features that are common in relational databases. Specifically, we can create indexes on our documents to make querying more efficient, apply constraints across multiple collections to ensure that the data remains consistent, and perform queries that span multiple collections in one go.

在此基础上,我们还可以使用关系型数据库中常见的一些功能。具体来说,我们可以在我们的文档上创建索引以使查询更有效率,在多个集合中应用约束条件以确保数据保持一致,并一次性执行跨越多个集合的查询。

Fauna’s query engine also has support for graph queries, allowing us to build complex data structures spanning multiple collections and access them all as if they were a single graph of data.

Fauna的查询引擎也支持图形查询,允许我们建立跨越多个集合的复杂数据结构,并像访问一个单一的数据图一样访问它们。

Finally, Fauna has temporal modeling facilities that can allow us to interact with our database at any point in its life. This means that not only can we see all of the changes that have happened to records over time, but we can directly access the data as it was at a given point in time.

最后,Fauna有时间模型设施,可以让我们在数据库的任何时间点与之互动。这意味着我们不仅可以看到记录随时间发生的所有变化,而且可以直接访问数据在某个特定时间点上的情况。

2.4. Multi-Tenant Database

2.4.多租户数据库

A multi-tenant database server means that it supports multiple different databases used by different users. This is very common with database engines used for cloud hosting since it means that one server can support many different customers.

多租户数据库服务器意味着它支持不同用户使用的多个不同的数据库。这在用于云主机的数据库引擎中非常常见,因为它意味着一台服务器可以支持许多不同的客户。

Fauna takes this in a slightly different direction. Instead of different tenants representing different customers within a single installed database engine, Fauna uses tenants to represent different subsets of data for a single customer.

Fauna在一个稍微不同的方向上采取了这种做法。Fauna使用租户来代表单个客户的不同数据子集,而不是在一个已安装的数据库引擎中代表不同的客户。

It is possible to create databases that are themselves children of other databases. We can then create credentials for accessing these child databases. However, where Fauna is different is that we can perform read-only queries against data from child databases of the one we’re connected to. However, it isn’t possible to access data in parent or sibling databases.

可以创建本身是其他数据库的子数据库。然后我们可以创建访问这些子数据库的凭证。然而,Fauna的不同之处在于,我们可以对我们所连接的数据库的子数据库的数据进行只读查询。然而,它不可能访问父数据库或兄弟数据库的数据。

This allows for us to create child databases for different services within the same parent database, and then have admin users query across all of the data in one go — this can be handy for analytics purposes.

这使我们能够在同一个父数据库中为不同的服务创建子数据库,然后让管理员用户一次性查询所有的数据–这对分析来说是很方便的。

2.5. Multi-Protocol Database

2.5.多协议数据库

This means that we have multiple different ways to access the same data.

这意味着我们有多种不同的方式来访问同一个数据。

The standard way to access our data is using the Fauna Query Language (FQL) via one of the provided drivers. This gives us access to the full abilities of the database engine, allowing us to access all of the data in any way we need.

访问我们数据的标准方式是通过所提供的驱动程序之一使用Fauna查询语言(FQL)。这使我们能够访问数据库引擎的全部能力,使我们能够以任何我们需要的方式访问所有的数据。

Alternatively, Fauna also exposes a GraphQL endpoint that we can use. The advantage of this is that we can use it from any application regardless of the programming language, rather than depending on dedicated drivers for our language. However, not all features are available over this interface. In particular, we’re required to create a GraphQL schema that describes the shape of our data ahead of time, which means that we’re not able to have different records in the same collection that have different shapes.

另外,Fauna也暴露了一个GraphQL端点,我们可以使用。这样做的好处是,我们可以从任何应用程序中使用它,而不需要依赖我们语言的专用驱动程序。然而,并不是所有的功能都可以通过这个接口使用。特别是,我们需要创建一个GraphQL模式,提前描述我们数据的形状,这意味着我们不能在同一个集合中拥有不同形状的不同记录。

3. Creating a Fauna Database

3.创建动物区系数据库

Now that we know what Fauna can do for us, let’s actually create a database for us to use.

现在我们知道Fauna可以为我们做什么,让我们实际创建一个数据库供我们使用。

If we don’t already have an account, we need to create one.

如果我们还没有账户,我们需要创建一个

Once we’ve logged in, on the dashboard, we simply click the “Create Database” link:

一旦我们登录,在仪表板上,我们只需点击 “创建数据库 “链接。

fauna create db

This then opens up a pane for the name and region of the database. We also have the option of pre-populating the database with some example data to see how it can work, to help us to get used to the system:

然后打开一个窗格,显示数据库的名称和区域。我们还可以选择用一些示例数据预先填充数据库,看看它如何工作,以帮助我们适应这个系统。

fauna db region

On this screen, the choice for “Region Group” is important, both for the amount that we’ll have to pay for anything past the free limits, but also for the endpoints that we need to use to connect to the database from outside.

在这个屏幕上,”区域组 “的选择很重要,这既是为了我们必须为超过免费限制的东西支付的金额,也是为了我们需要用来从外部连接到数据库的端点。

Once we’ve done this, we have a full database that we can use as needed. If we selected the demo data, then it comes complete with some populated collections, indexes, custom functions, and a GraphQL schema. If not, then the database is completely empty and ready for us to create our desired structure:

一旦我们完成了这些,我们就有了一个完整的数据库,我们可以根据需要使用。如果我们选择了演示数据,那么它就带有一些填充的集合、索引、自定义函数和GraphQL模式。如果没有,那么这个数据库就完全是空的,准备好让我们创建我们想要的结构。

fauna db structure

Finally, in order to connect to the database from outside, we need an authentication key. We can create one from the Security tab on the sidebar:

最后,为了从外部连接到数据库,我们需要一个认证密钥。我们可以在侧边栏的安全选项卡上创建一个。

fauna auth key

When creating a new key, make sure to copy it down because, for security reasons, there’s no way to get it back again after leaving the screen.

当创建一个新的密钥时,确保将其复制下来,因为出于安全原因,离开屏幕后没有办法再把它找回来。

4. Interacting with Fauna

4.与动物群互动

Now that we have a database, we can start working with it.

现在我们有了一个数据库,我们可以开始使用它了。

Fauna offers two distinct ways to read and write data in our database from outside: the FQL drivers and the GraphQL API. We also have access to the Fauna Shell, which allows us to execute arbitrary commands from within the web UI.

Fauna提供了两种不同的方式来从外部读写我们数据库中的数据:FQL驱动程序和GraphQL API。我们还可以访问Fauna Shell,它允许我们从Web UI中执行任意的命令。

4.1. Fauna Shell

4.1.动物界的贝壳

The Fauna Shell allows us to execute any commands from within the web UI. We can do this using any of our configured keys — acting exactly the same as if we’d connected from outside with that key — or else as certain special admin connections:

Fauna Shell允许我们从Web UI中执行任何命令。我们可以使用我们配置的任何一个键来做这件事–就像我们从外面用该键连接一样–或者作为某些特殊的管理连接。

fauna shell

This allows us to explore our data and test out queries that we want to be using from our application in a very low-friction manner.

这使我们能够探索我们的数据,并以一种非常低的方式测试我们希望从我们的应用程序中使用的查询。

4.2. Connecting with FQL

4.2.与FQL连接

If we want to instead connect our application to Fauna and use FQL, we need to use one of the provided drivers — including ones for Java and Scala.

如果我们想把我们的应用程序连接到Fauna并使用FQL,我们需要使用提供的驱动程序之一–包括用于Java和Scala的驱动程序。

The Java drivers require us to be running on Java 11 or higher.

Java驱动程序要求我们运行在Java 11或更高的版本上

The first thing we need to do is add in the dependency. If we’re using Maven, we’ll simply add it to our pom.xml file:

我们需要做的第一件事是添加依赖性。如果我们使用Maven,我们只需将其添加到pom.xml文件。

<dependency>
    <groupId>com.faunadb</groupId>
    <artifactId>faunadb-java</artifactId>
    <version>4.2.0</version>
    <scope>compile</scope>
</dependency>

We then need to create a client connection that we can use to communicate with the database:

然后我们需要创建一个客户端连接,用来与数据库通信。

FaunaClient client = FaunaClient.builder()
    .withEndpoint("https://db.us.fauna.com/")
    .withSecret("put-your-authorization-key-here")
    .build();

Note that we’ll need to provide the correct values for the database endpoint — which varies based on the region group that was selected when the database was created — and the secret key that we created earlier.

请注意,我们需要为数据库端点提供正确的值–它根据创建数据库时选择的区域组而有所不同–以及我们先前创建的秘钥。

This client will act as a connection pool, opening new connections to the database as needed for different queries. This means that we can create it once at the start of our application and re-use it as much as we need.

该客户端将作为一个连接池,根据不同的查询需要打开新的数据库连接。这意味着我们可以在应用程序开始时创建一次,并根据需要重复使用它。

If we have a need to connect with different secrets, this will need to be different clients. For example, if we want to interact with multiple different child databases within the same parent database.

如果我们有需要与不同的秘密连接,这将需要不同的客户端。例如,如果我们想在同一个父数据库中与多个不同的子数据库互动。

Now that we have a client, we can use it to send queries to the database:

现在我们有了一个客户端,我们可以用它来向数据库发送查询。

client.query(
    language.Get(language.Ref(language.Collection("customers"), 101))
).get();

4.3. Connecting with GraphQL

4.3.与GraphQL的连接

Fauna offers a complete GraphQL API for interacting with our database. This can allow us to use the database without any special drivers, needing nothing more than an HTTP client.

Fauna提供了一个完整的GraphQL API,用于与我们的数据库进行交互。这可以让我们在没有任何特殊驱动的情况下使用数据库,只需要一个HTTP客户端。

In order to use GraphQL support, we need to first create a GraphQL schema. This will define the schema itself and how it maps onto our pre-existing Fauna database constructs — such as collections, indexes, and functions. Once done, any GraphQL-aware client — or even just an HTTP client such as RestTemplate — can be used to call our database.

为了使用 GraphQL 支持,我们需要首先创建一个 GraphQL 模式。这将定义模式本身,以及它如何映射到我们预先存在的 Fauna 数据库结构中,例如集合、索引和函数。一旦完成,任何GraphQL感知的客户端–甚至只是一个HTTP客户端,如RestTemplate–都可以用来调用我们的数据库。

Note that this will only allow us to interact with the data in our database. If we wish to use any administrative commands — such as creating new collections or indexes — then this requires either an FQL command or else the web admin UI.

请注意,这只允许我们与数据库中的数据互动。如果我们希望使用任何管理命令–例如创建新的集合或索引–那么这需要FQL命令或Web管理界面。

Connecting to Fauna via GraphQL requires us to use the correct URL — https://graphql.us.fauna.com/graphql for the US region — and to provide our authentication key as a bearer token within the Authorization header. At this point, we can use it as any normal GraphQL endpoint, by making POST requests to the URL and providing the query or mutation in the body, optionally with any variables to use with them.

通过GraphQL连接到Fauna需要我们使用正确的URL–美国地区的https://graphql.us.fauna.com/graphql,并在Authorization头中提供我们的认证密钥作为承载令牌。在这一点上,我们可以像任何正常的GraphQL端点一样使用它,方法是向URL发出POST请求,并在正文中提供查询或突变,还可以选择与它们一起使用的任何变量。

5. Using Fauna from Spring

5.使用Spring中的Fauna

Now that we understand what Fauna is and how to use it, we can see how to integrate it into our Spring applications.

现在我们了解了Fauna是什么以及如何使用它,我们可以看看如何将它整合到我们的Spring应用程序中。

Fauna doesn’t have any native Spring drivers. Instead, we’ll be configuring the normal Java drivers as Spring beans to use within our application.

Fauna没有任何本地的Spring驱动。相反,我们将把普通的Java驱动配置成Spring Bean,在我们的应用程序中使用。

5.1. Fauna Configuration

5.1.动物群配置

Before we can make use of Fauna, we need some configuration. Specifically, we need to know the region that our Fauna database is in — from which we can then derive the appropriate URLs — and we need to know a secret that we can use to connect to the database.

在我们利用Fauna之前,我们需要一些配置。具体而言,我们需要知道我们的Fauna数据库所处的区域–然后我们可以从中得出适当的URL–并且我们需要知道一个我们可以用来连接到数据库的秘密。

For this, we will add properties for fauna.region and fauna.secret to our application.properties file — or any other supported Spring configuration method:

为此,我们将把fauna.regionfauna.secret的属性添加到我们的application.properties文件–或任何其他支持的Spring配置方法

fauna.region=us
fauna.secret=FaunaSecretHere

Note that we’re defining the Fauna region here instead of the URLs. This allows us to correctly derive the URL for both FQL and GraphQL from the same setting. This avoids the risk that we might configure the two URLs differently.

注意,我们在这里定义了Fauna区域,而不是URLs。这使我们能够从同一设置中正确推导出FQL和GraphQL的URL。这避免了我们可能以不同方式配置这两个URL的风险。

5.2. FQL Client

5.2.FQL客户端

If we’re planning on using FQL from our application, we can add a FaunaClient bean to the Spring context. This will involve creating a Spring configuration object to consume the appropriate properties and construct the FaunaClient object:

如果我们计划从我们的应用程序中使用FQL,我们可以在Spring上下文中添加一个FaunaClient Bean。这将涉及创建一个Spring配置对象,以消耗适当的属性并构建FaunaClient对象。

@Configuration
class FaunaClientConfiguration {
    @Value("https://db.${fauna.region}.fauna.com/")
    private String faunaUrl;

    @Value("${fauna.secret}")
    private String faunaSecret;

    @Bean
    FaunaClient getFaunaClient() throws MalformedURLException {
        return FaunaClient.builder()
            .withEndpoint(faunaUrl)
            .withSecret(faunaSecret)
            .build();
    }
}

This lets us use the FaunaClient directly from anywhere in our application, the same way that we would use JdbcTemplate for access to a JDBC database. We also have the opportunity to wrap this in a higher-level object to work in domain-specific terms if we so wish.

这让我们可以在应用程序的任何地方直接使用FaunaClient,就像我们使用JdbcTemplate来访问JDBC数据库一样。如果我们愿意的话,我们也有机会将其包裹在一个更高级别的对象中,以便在特定领域内工作。

5.3. GraphQL Client

5.3.GraphQL客户端

If we’re planning on using GraphQL to access Fauna, there is a little more work involved. There’s no standard client for calling GraphQL APIs. Instead, we’ll use the Spring RestTemplate to make standard HTTP requests to the GraphQL endpoint. The newer WebClient would work equally well if we were building a WebFlux-based application.

如果我们打算使用GraphQL来访问Fauna,就会涉及更多的工作。没有用于调用GraphQL API的标准客户端。相反,我们将使用Spring RestTemplate来向GraphQL端点发出标准的HTTP请求。如果我们正在构建一个基于WebFlux的应用程序,较新的WebClient也同样适用。

To achieve this, we’ll write a class that wraps the RestTemplate and can make appropriate HTTP calls to Fauna:

为了实现这一目标,我们将编写一个类,它可以包装RestTemplate,并可以对Fauna进行适当的HTTP调用。

@Component
public class GraphqlClient {
    @Value("https://graphql.${fauna.region}.fauna.com/graphql")
    private String faunaUrl;

    @Value("${fauna.secret}")
    private String faunaSecret;

    private RestTemplate restTemplate = new RestTemplate();

    public <T> T query(String query, Class<T> cls) {
        return query(query, Collections.emptyMap(), cls);
    }

    public <T, V> T query(String query, V variables, Class<T> cls) {
        var body = Map.of("query", query, "variables", variables);

        var request = RequestEntity.post(faunaUrl)
            .header("Authorization", "Bearer " + faunaSecret)
            .body(body);
        var response = restTemplate.exchange(request, cls);

        return response.getBody();
    }
}

This client allows us to make GraphQL calls to Fauna from other components of our application. We have two methods, one that just takes a GraphQL query string and another that additionally takes some variables to use with it.

这个客户端允许我们从我们应用程序的其他组件对Fauna进行GraphQL调用。我们有两个方法,一个是只接受GraphQL查询字符串,另一个是额外接受一些变量来使用它。

They also both take the type to deserialize the query result into. Using this will handle all of the details of talking to Fauna, allowing us to concentrate on our application needs instead.

他们也都采取了将查询结果反序列化的类型。使用这个将处理与Fauna对话的所有细节,使我们能够专注于我们的应用需求。

6. Summary

6.总结

In this article, we have had a brief introduction to the Fauna database, seeing some of the features that it offers that can make it a highly compelling choice for our next project, as well as seeing how we can interact with it from our application.

在这篇文章中,我们对Fauna数据库进行了简单的介绍,看到了它所提供的一些功能,这些功能可以使它成为我们下一个项目中非常有说服力的选择,以及看到我们如何从我们的应用程序中与它进行交互。

Why not explore some of the features we’ve mentioned here in your next project?

为什么不在你的下一个项目中探索我们在这里提到的一些功能?