|
此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Cassandra 4.5.2! |
持久化实体
这CassandraTemplate类(及其响应式变体ReactiveCassandraTemplate),位于org.springframework.data.cassandrapackage,是 Spring 的 Cassandra 支持中的中心类,并提供了与数据库交互的丰富功能集。该模板提供了创建、更新、删除和查询 Cassandra 的便利作,并提供了域对象和 Cassandra 表中行之间的映射。
| 配置后,模板实例是线程安全的,可以在多个实例之间重复使用。 |
Cassandra 中的行与应用程序域类之间的映射是通过委托给CassandraConverter接口。 Spring 提供了一个默认实现MappingCassandraConverter,但您也可以编写自己的自定义转换器。有关更多详细信息,请参阅有关 Cassandra 转换的部分。
这CassandraTemplate类实现了CassandraOperations接口及其反应性变体ReactiveCassandraTemplate实现ReactiveCassandraOperations. 在尽可能多的方法中[Reactive]CassandraOperations以 Cassandra 中可用的方法命名,以使已经熟悉 Cassandra 的开发人员熟悉该 API。
例如,您可以找到select,insert,delete和update. 设计目标是尽可能轻松地在使用基本 Cassandra 驱动程序和[Reactive]CassandraOperations. 这两个 API 之间的主要区别在于CassandraOperations可以传递域对象,而不是 CQL 和查询对象。
引用作的首选方式[Reactive]CassandraTemplate实例通过[Reactive]CassandraOperations接口。 |
使用的默认转换器实现[Reactive]CassandraTemplate是MappingCassandraConverter. 而MappingCassandraConverter可以使用其他元数据来指定对象到行的映射,它还可以通过使用一些约定来映射字段和表名来转换不包含其他元数据的对象。这些约定以及映射注释的使用在“映射”一章中进行了解释。
另一个核心特征[Reactive]CassandraTemplate是将Cassandra Java驱动程序中抛出的异常转换为Spring的可移植数据访问异常层次结构的异常。有关更多信息,请参阅有关异常转换的部分。
模板 API 具有不同的执行模型风格。基本CassandraTemplate使用阻塞(命令式同步)执行模型。您可以使用AsyncCassandraTemplate用于异步执行和同步ListenableFutureinstances 或ReactiveCassandraTemplate用于反应性执行。 |
实例化CassandraTemplate
CassandraTemplate应该始终配置为 Spring bean,尽管我们之前展示了一个示例,您可以在其中直接实例化它。但是,因为我们假设的是创建 Spring 模块的上下文,所以我们假设存在 Spring 容器。
有两种方法可以获得CassandraTemplate,具体取决于您加载 Spring 的方式ApplicationContext:
自动接线
您可以自动连接[Reactive]CassandraOperations到项目中,如以下示例所示:
-
Imperative
-
Reactive
@Autowired
private CassandraOperations cassandraOperations;
@Autowired
private ReactiveCassandraOperations reactiveCassandraOperations;
与所有 Spring 自动布线一样,这假设只有一个类型的 bean[Reactive]CassandraOperations在ApplicationContext. 如果您有多个[Reactive]CassandraTemplatebean(如果你在同一项目中使用多个键空间,则会出现这种情况),那么你可以使用@Qualifier注释来指定要自动连接的 bean。
-
Imperative
-
Reactive
@Autowired
@Qualifier("keyspaceOneTemplateBeanId")
private CassandraOperations cassandraOperations;
@Autowired
@Qualifier("keyspaceOneTemplateBeanId")
private ReactiveCassandraOperations reactiveCassandraOperations;
Bean 查找ApplicationContext
您还可以查找[Reactive]CassandraTemplatebean 来自ApplicationContext,如以下示例所示:
-
Imperative
-
Reactive
CassandraOperations cassandraOperations = applicationContext.getBean("cassandraTemplate", CassandraOperations.class);
ReactiveCassandraOperations cassandraOperations = applicationContext.getBean("ReactiveCassandraOperations", ReactiveCassandraOperations.class);
查询行
您可以使用Query和Criteria类,其方法名称反映了本机 Cassandra 谓词运算符名称,例如lt,lte,is,等。
这Query和Criteria类遵循流畅的 API 风格,因此可以轻松地将多个方法条件和查询链接在一起,同时拥有易于理解的代码。
在 Java 中创建静态导入时使用Query和Criteria实例以提高可读性。
查询表中的行
在前面的部分中,我们看到了如何使用selectOneById方法[Reactive]CassandraTemplate.
这样做会返回单个域对象。
我们还可以查询要作为域对象列表返回的行集合。
假设我们有多个Person对象的名称和年龄值存储为表中的行,并且每个人都有一个帐户余额,我们现在可以使用以下代码运行查询:
[Reactive]CassandraTemplate-
Imperative
-
Reactive
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
List<Person> result = cassandraTemplate.select(query(where("age").is(50))
.and(where("balance").gt(1000.00d)).withAllowFiltering(), Person.class);
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
Flux<Person> result = reactiveCassandraTemplate.select(query(where("age").is(50))
.and(where("balance").gt(1000.00d)).withAllowFiltering(), Person.class);
这select,selectOne和stream方法采用Queryobject 作为参数。
此对象定义用于执行查询的条件和选项。
该条件是通过使用Criteria具有名为where实例化一个新的Criteria对象。
我们建议使用 静态导入org.springframework.data.cassandra.core.query.Criteria.where和Query.query,使查询更具可读性。
此查询应返回Person满足指定条件的对象。
这Criteriaclass 具有以下方法,这些方法对应于 Apache Cassandra 中提供的运算符:
Criteria类的方法
-
CriteriaDefinition燃气轮机(Object value):使用运算符创建条件。> -
CriteriaDefinitionGTE(Object value):使用运算符创建条件。>= -
CriteriaDefinition在(Object… values):使用INvarargs 参数的运算符。 -
CriteriaDefinition在(Collection<?> collection):使用IN运算符。 -
CriteriaDefinition是(Object value):使用字段匹配 (column = value). -
CriteriaDefinitionLT(Object value):使用运算符创建条件。< -
CriteriaDefinitionLTE(Object value):使用⇐算子。 -
CriteriaDefinition喜欢(Object value):使用LIKE算子。 -
CriteriaDefinition包含(Object value):使用CONTAINS算子。 -
CriteriaDefinition包含键(Object key):使用CONTAINS KEY算子。
Criteria一旦创建,就不可变。
Query 类的方法
这Queryclass 有一些额外的方法,您可以使用它们来为查询提供选项:
-
Query由(CriteriaDefinition… criteria):用于创建Query对象。 -
Query和(CriteriaDefinition criteria):用于向查询添加其他条件。 -
Query列(Columns columns):用于定义要包含在查询结果中的列。 -
Query限制(Limit limit):用于将返回结果的大小限制为提供的限制(使用SELECT限制)。 -
Query限制(long limit):用于将返回结果的大小限制为提供的限制(使用SELECT限制)。 -
Query页面请求(Pageable pageRequest):用于关联Sort,PagingState和fetchSize替换为查询(用于分页)。 -
Querypaging状态(ByteBuffer pagingState):用于关联ByteBuffer替换为查询(用于分页)。 -
Query查询选项(QueryOptions queryOptions):用于关联QueryOptions与查询。 -
Query排序(Sort sort):用于为结果提供排序定义。 -
QuerywithAllowFiltering :用于渲染()ALLOW FILTERING查询。
Query创建后是不可变的。调用方法会创建新的不可变(中间)Query对象。
查询行的方法
这Queryclass 具有以下返回行的方法:
-
List<T>选择(Query query, Class<T> entityClass):查询类型T从桌子上。 -
T选择一个(Query query, Class<T> entityClass):查询类型的单个对象T从桌子上。 -
Slice<T>片(Query query, Class<T> entityClass):通过查询Slice类型的对象T从桌子上。 -
Stream<T>流(Query query, Class<T> entityClass):查询类型为的对象流T从桌子上。 -
List<T>选择(String cql, Class<T> entityClass):对类型T通过提供 CQL 语句来从表中。 -
T选择一个(String cql, Class<T> entityClass):针对类型为T通过提供 CQL 语句来从表中。 -
Stream<T>流(String cql, Class<T> entityClass):对类型为T通过提供 CQL 语句来从表中。
查询方法必须指定目标类型T返回。
Fluent 模板 API
这[Reactive]CassandraOperations接口是与 Apache Cassandra 进行更低级交互的核心组件之一。
它提供了广泛的方法。
您可以找到每种方法的多个重载。
它们中的大多数都涵盖了 API 的可选(可为 null)部分。
FluentCassandraOperations及其反应性变体ReactiveFluentCassandraOperations为常用方法提供更窄的接口[Reactive]CassandraOperations提供更易读、更流畅的 API。
入口点 (query(…),insert(…),update(…)和delete(…))遵循基于要执行的作的自然命名方案。
从入口点开始,该 API 旨在仅提供依赖于上下文的方法,引导开发人员使用调用实际[Reactive]CassandraOperations.
以下示例显示了 fluent API:
-
Imperative
-
Reactive
List<SWCharacter> all = ops.query(SWCharacter.class)
.inTable("star_wars") (1)
.all();
| 1 | 如果出现以下情况,请跳过此步骤SWCharacter使用@Table或者使用类名作为表名不是问题 |
Flux<SWCharacter> all = ops.query(SWCharacter.class)
.inTable("star_wars") (1)
.all();
| 1 | 如果出现以下情况,请跳过此步骤SWCharacter使用@Table或者使用类名作为表名不是问题 |
如果 Cassandra 中的表包含不同类型的实体,例如Jedi在SWCharacters,可以使用不同的类型来映射查询结果。
您可以使用as(Class<?> targetType)将结果映射到不同的目标类型,而query(Class<?> entityType)仍然适用于查询和表名。
以下示例使用query和as方法:
-
Imperative
-
Reactive
List<Jedi> all = ops.query(SWCharacter.class) (1)
.as(Jedi.class) (2)
.matching(query(where("jedi").is(true)))
.all();
| 1 | 查询字段映射到SWCharacter类型。 |
| 2 | 生成的行映射到Jedi. |
Flux<Jedi> all = ops.query(SWCharacter.class) (1)
.as(Jedi.class) (2)
.matching(query(where("jedi").is(true)))
.all();
| 1 | 查询字段映射到SWCharacter类型。 |
| 2 | 生成的行映射到Jedi. |
终止方法 (first(),one(),all()和stream()) 处理检索单个实体和检索多个实体之间的切换,因为List或Stream和类似的作。
新的 fluent 模板 API 方法(即query(…),insert(…),update(…)和delete(…)) 使用有效的线程安全支持对象来组成 CQL 语句。
然而,它会带来额外的年轻一代 JVM 堆开销,因为该设计基于各种 CQL 语句组件的最终字段和突变的构造。
在可能插入或删除大量对象时(例如,在循环中)时,您应该小心。 |
矢量搜索查询
投影是查询以与实体不同的形式返回的数据的基础。
虽然 Cassandra 的主要用例遵循键值模型(存储您检索到的内容),但向量搜索则不同。
运行矢量搜索查询通常会产生聚合或类似报告的结果集。
典型的查询将返回某种形式的内容(例如textcolumn)及其到实际向量的分数(或距离)。
考虑以下域模型:
@Table
class Comments {
@Id UUID id;
String comment;
@VectorType(dimensions = 5)
@SaiIndexed Vector vector;
}
class CommentSearch {
String comment;
float similarity;
}
Comments是定义向量列的域类型,并且comment列。
运行向量搜索需要使用ANN排序,并且通常会定义一个相似性函数来确定其与给定向量的距离。
Columns columns = Columns.from("comment") (1)
.select("vector", builder -> builder.similarity(vector)
.cosine().as("similarity")); (2)
Query query = Query.select(columns)
.limit(3)
.sort(VectorSort.ann("vector", vector)); (3)
template.query(Comments.class)
.as(CommentSearch.class) (4)
.matching(query)
.all();
| 1 | 选择要查询的列。 |
| 2 | 将函数调用similarity_cosine(vector, […]).Columns.select(…)使用选择器构建器定制器来配置实际选择。
确保声明要将结果映射到的别名CommentSearch.similarity作为结果映射使用列名称。 |
| 3 | 使用VectorSort.ann(…)定义与给定向量相比的排序顺序。 |
| 4 | 定义要将结果映射到的目标类型。
这CommentSearchtype 是 结果投影类型,定义映射结果的列。 |
保存、更新和删除行
[Reactive]CassandraTemplate为您提供了一种保存、更新和删除域对象并将这些对象映射到 Cassandra 中管理的表的简单方法。
插入和更新行的方法
[Reactive]CassandraTemplate有几种保存和插入对象的便捷方法。
要对转换过程进行更细粒度的控制,您可以注册 SpringConverter实例与MappingCassandraConverter(例如,Converter<Row, Person>).
插入和更新作之间的区别在于INSERT作不插入null值。 |
使用INSERT作是保存一个 POJO。
在这种情况下,表名由简单类名(而不是完全限定的类名)决定。
可以使用映射元数据覆盖用于存储对象的表。
插入或更新时,id属性。
Apache Cassandra 无法生成 ID。
以下示例使用保存作并检索其内容:
[Reactive]CassandraTemplate-
Imperative
-
Reactive
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
Person bob = new Person("Bob", 33);
cassandraTemplate.insert(bob);
Person queriedBob = cassandraTemplate.selectOneById(query(where("age").is(33)), Person.class);
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
…
Person bob = new Person("Bob", 33);
cassandraTemplate.insert(bob);
Mono<Person> queriedBob = reactiveCassandraTemplate.selectOneById(query(where("age").is(33)), Person.class);
可以使用以下作进行插入和保存:
-
void插入(Object objectToSave):将对象插入 Apache Cassandra 表中。 -
WriteResult插入(Object objectToSave, InsertOptions options):将对象插入 Apache Cassandra 表中并应用InsertOptions.
您可以使用以下更新作:
-
void更新(Object objectToSave):更新 Apache Cassandra 表中的对象。 -
WriteResult更新(Object objectToSave, UpdateOptions options):更新 Apache Cassandra 表中的对象并应用UpdateOptions.
您还可以使用老式方法并编写自己的 CQL 语句,如以下示例所示:
-
Imperative
-
Reactive
String cql = "INSERT INTO person (age, name) VALUES (39, 'Bob')";
cassandraTemplate().getCqlOperations().execute(cql);
String cql = "INSERT INTO person (age, name) VALUES (39, 'Bob')";
Mono<Boolean> applied = reactiveCassandraTemplate.getReactiveCqlOperations().execute(cql);
您还可以在使用 TTL、一致性级别和轻量级事务时配置其他选项InsertOptions和UpdateOptions.
我的行插入到哪个表中?
您可以通过两种方式管理用于对表进行作的表名称。
默认表名是更改为以小写字母开头的简单类名。
因此,一个实例com.example.Person类将存储在person桌子。
第二种方法是在@Table注解。
批量插入、更新和删除单个对象
Cassandra 协议支持使用批处理在一个作中插入行的集合。
以下方法[Reactive]CassandraTemplate接口支持此功能:
-
batchOps:创建新的[Reactive]CassandraBatchOperations以填充批次。
[Reactive]CassandraBatchOperations
-
insert:接受单个对象、数组 (var-args) 或Iterable要插入的对象。 -
update:接受单个对象、数组 (var-args) 或Iterable要更新的对象数。 -
delete:接受单个对象、数组 (var-args) 或Iterable要删除的对象数。 -
withTimestamp:将 TTL 应用于批处理。 -
execute:执行批处理。
更新表中的行
对于更新,您可以选择更新多行。
以下示例演示了通过向分配的余额添加一次性 50.00 美元奖金来更新单个帐户对象:+
[Reactive]CasandraTemplate-
Imperative
-
Reactive
import static org.springframework.data.cassandra.core.query.Criteria.where;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
…
boolean applied = cassandraTemplate.update(Query.query(where("id").is("foo")),
Update.create().increment("balance", 50.00), Account.class);
import static org.springframework.data.cassandra.core.query.Criteria.where;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
…
Mono<Boolean> wasApplied = reactiveCassandraTemplate.update(Query.query(where("id").is("foo")),
Update.create().increment("balance", 50.00), Account.class);
除了Query前面讨论过,我们通过使用Update对象。
这Updateclass 具有与 Apache Cassandra 可用的更新分配匹配的方法。
大多数方法返回Update对象,为代码样式目的提供流畅的 API。
执行行更新的方法
update 方法可以更新行,如下所示:
-
boolean更新(Query query, Update update, Class<?> entityClass):更新 Apache Cassandra 表中的所选对象。
Update 类的方法
这Updateclass 可以与一点“语法糖”一起使用,因为它的方法旨在链接在一起。
此外,您还可以启动创建新的Update实例与静态方法public static Update update(String key, Object value)以及使用静态导入。
这Updateclass 有以下方法:
-
AddToBuilderaddTo(String columnName)AddToBuilder入口点:-
更新
prepend(Object value):使用更新分配将集合值附加到现有集合前面。+ -
更新
prependAll(Object… values):使用更新分配将所有集合值添加到现有集合的前面。+ -
更新
append(Object value):使用更新分配将集合值追加到现有集合。+ -
更新
append(Object… values):使用更新分配将所有集合值追加到现有集合。+ -
更新
entry(Object key, Object value):使用更新分配添加地图条目。+ -
更新
addAll(Map<? extends Object, ? extends Object> map):使用更新分配将所有地图条目添加到地图中。+
-
-
Update删除(String columnName, Object value):使用更新分配从集合中删除值。- -
UpdateClear(String columnName):清除集合。 -
Update增加(String columnName, Number delta):使用更新分配进行更新。+ -
Update递减(String columnName, Number delta):使用更新分配进行更新。- -
Update设置(String columnName, Object value):使用更新分配进行更新。= -
SetBuilder设置(String columnName)SetBuilder入口点:-
更新
atIndex(int index).to(Object value):使用更新赋值将给定索引处的集合设置为值。= -
更新
atKey(String object).to(Object value):将给定键处的映射条目设置为更新分配的值。=
-
以下列表显示了一些更新示例:
// UPDATE … SET key = 'Spring Data';
Update.update("key", "Spring Data")
// UPDATE … SET key[5] = 'Spring Data';
Update.empty().set("key").atIndex(5).to("Spring Data");
// UPDATE … SET key = key + ['Spring', 'DATA'];
Update.empty().addTo("key").appendAll("Spring", "Data");
请注意Update创建后是不可变的。调用方法会创建新的不可变(中间)Update对象。
删除行的方法
您可以使用以下重载方法从数据库中删除对象:
-
boolean删除(Query query, Class<?> entityClass):删除由Query. -
T删除(T entity):删除给定对象。 -
T删除(T entity, QueryOptions queryOptions):删除应用QueryOptions. -
booleandeleteById(Object id, Class<?> entityClass):使用给定的 ID 删除对象。
乐观锁定
这@Version注释提供类似于 Cassandra 上下文中的 JPA 语法,并确保更新仅应用于具有匹配版本的行。
Optimistic Locking 利用 Cassandra 的轻量级事务有条件地插入、更新和删除行。
因此INSERT语句使用IF NOT EXISTS条件。
对于更新和删除,version 属性的实际值将添加到UPDATE条件,如果另一个作同时更改了行,则修改不会产生任何影响。
在这种情况下,一个OptimisticLockingFailureException被抛出。
以下示例显示了这些功能:
@Table
class Person {
@Id String id;
String firstname;
String lastname;
@Version Long version;
}
Person daenerys = template.insert(new Person("Daenerys")); (1)
Person tmp = template.findOne(query(where("id").is(daenerys.getId())), Person.class); (2)
daenerys.setLastname("Targaryen");
template.save(daenerys); (3)
template.save(tmp); // throws OptimisticLockingFailureException (4)
| 1 | 首先插入文档。version设置为0. |
| 2 | 加载刚刚插入的文档。version还是0. |
| 3 | 使用version = 0.
将lastname和颠簸version自1. |
| 4 | 尝试更新之前加载的文档,该文档仍然有version = 0.
该作失败,并显示OptimisticLockingFailureException,作为当前version是1. |
| 乐观锁定仅支持单实体作,不支持批处理作。 |