# 12.2.4 编写响应式 Cassandra 库

正如您在第 3 章中看到的，编写一个基于 Spring Data 的 Repository，只需要声明扩展自 Spring Data 的基础接口。以及有选择性地，声明一些自定义查询的其他查询方法。事实证明，编写响应式 Repository 也没有什么不同。主要的区别是，要扩展不同的接口。您的方法将要处理响应式的 Mono 和 Flux，而不是实体类型和集合。

在编写响应式 Cassandra Repository 时，您可以选择继承两个基本接口：`ReactiveCassandraRepository` 和 `ReactiveCrudRepository`。我们的选择主要取决于 Repository 的使用方式。`ReactiveCassandraRepository` 扩展了 `ReactiveCrudRepository`，提供了 `insert()` 的一些重载方法。当要保存的对象是新建对象时，该方法会进行一些优化。其他方面，`ReactiveCassandraRepository` 提供与 `ReactiveCrudepository` 相同的操作。如果要插入大量的数据，您可能会选择`ReactiveCassandraRepository`。否则，最好使用 `ReactiveCrudRepository` ，因为它在兼容其他数据库类型上更方便。

> 我的 Cassandra Repository 必须是响应式的吗？
>
> 尽管本章描述的，是使用 Spring Data 编写响应式 Repository，但是您可能也有兴趣了解一下，其实也可以为 Cassandra 编写非响应式的 Repository。非响应式 Repository 需要扩展 `CassandraRepository` 接口，而不是扩展 `ReactiveCassandraRepository` 或 `ReactiveCrudRepository` 接口。然后您的 Repository 方法，可以简单地返回添加了 Cassandra 注解的实体类型或这些类型的集合，而不是 Flux 和 Mono。
>
> 如果您决定使用非响应式 Repository，还可以更改依赖，改为依赖于 `spring-boot-starter-data-cassandra` 而不是 `spring-bootstarter-data-cassandra-reactive`，虽然并不强制您要这样做。

先来看一下，您为 Taco Cloud 应用程序已经编写的一些 Repository。要让这些 Repository 变成响应式的，您应该做的第一个修改是，变更它们的父类。让它们扩展 `ReactiveCrudRepository` 或 `ReactiveCassandraRepository`， 而不是 `CrudRepository`。例如，对 `IngredientRepository` 来说，除了初始化数据库之外，您不会插入很多新的数据。因此，可以直接修改父类接口为 `RectiveCrudRepository`，如图所示：

```java
public interface IngredientRepository
            extends ReactiveCrudRepository<Ingredient, String> {
}
```

您从未在 IngredientRepository 中定义任何自定义查询方法，因此使 IngredientRepository 成为响应应式 Repository 没有什么更多的工作。但是，因为它现在扩展了 `ReactiveCrudRepository`，所以它的方法将处理 Flux 和 Mono。例如 `findAll()` 方法，现在返回 `Flux<Ingredient>` 而不是 `Iterable<Ingredient>`。因此，您需要确保，无论在何处都以正确的方式使用数据。例如，`IngredientController` 中的 `allIngredients()` 方法需要重写，以返回 `Flux<Ingredient>`：

```java
@GetMapping
public Flux<Ingredient> allIngredients() {
    return repo.findAll();
}
```

对 `TacoRepository` 的修改，只是细节上更复杂一些。首先不再扩展 `PagingAndSortingRepository` 接口，而是扩展 `ReactiveCassandraRepository`。另外 Taco 对象的 ID 属性，也不再是 Long 型，而是 UUID:

```java
public interface TacoRepository
            extends ReactiveCrudRepository<Taco, UUID> {
}
```

因为这个新的 TacoRepository 的 `findAll()` 方法返回 `Flux<Ingredient>`，您不再费心考虑扩展 `PagingAndSortingRepository` 以处理分页。相反，`DesignTacoController` 中的 `recentTacos()` 方法只需要对返回的 Flux 调用 `take()` 方法，以限制数量。（事实上，在 11.1.2 节，您已经对 `DesignTacoController` 的 `recentTacos()` 方法进行了修改）

对 `OrderRepository` 所需做的更改也很简单。不再扩展 `CrudRepository`，而是扩展 `ReactiveCassandraRepository`：

```java
public interface OrderRepository
            extends ReactiveCassandraRepository<Order, UUID> {
}
```

最后，让我们看看 `UserRepository`。您还记得，`UserRepository` 有一个自定义的查询方法 `findByUsername()`。这个方法给您的 Cassandra 持久化修改带来一点困难。应该像如下这样修改：

```java
public interface UserRepository
            extends ReactiveCassandraRepository<User, UUID> {

    @AllowFiltering
    Mono<User> findByUsername(String username);
}
```

所有接口所做的修改都基本一样（除了 `IngredientRepository`），现在 `UserRepository` 也扩展了 `ReactiveCassandraRepository`，看起来都很正常。但是它的 `findByUsername()` 方法需要引起额外注意。

首先，因为这是响应式的，所以 `findByUsername()` 不能再返回简单的实体对象 User。需要重新定义它以返回 `Mono<User>`。一般来说，您在任何一个响应式的自定义查询方法中，都应该返回一个 Mono（如果没有多个值）或 Flux （如果返回了多个值）。

另外，Cassandra 的特性决定了，您不能像关系型数据库 SQL 查询中那样，简单地使用 where 子句。 Cassandra 对读取数据高度优化，使用 where 子句进行过滤，可能会减慢一个本来很快速的查询。即便如此，对查询结果所在的表，按一个或多个列过滤，可能在业务上是必要的。这种情况下，使用 `@AllowFiltering` 注解标识允许过滤结果，为这些特殊情况提供解决办法。

在 `findByUsername()` 中，您可能希望看到如下 CQL 查询：

```sql
select * from users where username='some username';
```

再说一遍，这是 Cassandra 不允许的。但是当 `@AllowFiltering` 注解加在 `findByUsername()` 上，会得到如下所示的 CQL 查询：

```
select * from users where username='some username' allow filtering;
```

查询末尾的 allow filtering 子句，提醒 Cassandra 您知道，这会对查询性能有潜在影响，但您仍然需要它。在这种情况下，Cassandra 将允许 where 子句并相应地过滤结果。

Cassandra 很强大，当它与 Spring Data 和 Reactor 结合，您就可以在 Spring 应用程序中体验到这些强大功能。现在让我们转移注意力，来看看另一个对响应式 Repository 支持的数据库：MongoDB。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://potoyang.gitbook.io/spring-in-action-v5/di-12-zhang-xiang-ying-shi-chi-jiu-hua-shu-ju/12.2-shi-yong-xiang-ying-shi-cassandra-ku/12.2.4-bian-xie-xiang-ying-shi-cassandra-ku.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
