CQL Template API
CqlTemplate 类(及其响应式变体 ReactiveCqlTemplate)是核心 CQL 包中的核心类。它负责资源的创建与释放。它执行核心 CQL 工作流的基本任务,例如语句的创建和执行,而把提供 CQL 和提取结果的工作留给应用代码。CqlTemplate 类执行 CQL 查询和更新语句,遍历 ResultSet 实例并提取返回的参数值。它还捕获 CQL 异常,并将其转换为 org.springframework.dao 包中定义的通用、信息更丰富的异常层次结构。
当您在代码中使用 CqlTemplate 时,只需实现回调接口,这些接口有着明确定义的契约。给定一个 CqlSession,PreparedStatementCreator 回调接口会用所提供的 CQL 和任何必要的参数参数创建一个预编译语句。RowCallbackHandler 接口从 ResultSet 的每一行中提取值。
CqlTemplate 可以在 DAO 实现中使用,方式是直接用 SessionFactory 引用实例化,或在 Spring 容器中配置并以 bean 引用的形式提供给 DAO。CqlTemplate 是 CassandraTemplate 的基础构建块。
该类发出的所有 CQL 都会在 DEBUG 级别记录到与模板实例全限定类名对应的日志类别下(通常是 CqlTemplate,但如果您使用 CqlTemplate 类的自定义子类,则可能不同)。
您可以通过在 CQL API 实例上配置提取大小、一致性级别和重试策略的默认值来进行控制:CqlTemplate、AsyncCqlTemplate 和 ReactiveCqlTemplate。如果某个查询选项未设置,则应用默认值。
CqlTemplate 有不同的执行模型风格。基本的 CqlTemplate 使用阻塞执行模型。您可以使用 AsyncCqlTemplate 进行异步执行并与 CompletableFuture 实例同步,或使用 ReactiveCqlTemplate 进行响应式执行。
CqlTemplate 类用法示例
本节提供了一些 CqlTemplate 类的实际用法示例。这些示例并非 CqlTemplate 所暴露的全部功能的详尽清单。有关全部功能,请参阅 Javadoc。
用 CqlTemplate 查询(SELECT)
以下查询获取表中的行数:
- Imperative
- Reactive
int rowCount = cqlTemplate.queryForObject("SELECT COUNT(*) FROM t_actor", Integer.class);
Mono<Integer> rowCount = reactiveCqlTemplate.queryForObject("SELECT COUNT(*) FROM t_actor", Integer.class);
以下查询使用了绑定变量:
- Imperative
- Reactive
int countOfActorsNamedJoe = cqlTemplate.queryForObject(
"SELECT COUNT(*) FROM t_actor WHERE first_name = ?", Integer.class, "Joe");
Mono<Integer> countOfActorsNamedJoe = reactiveCqlTemplate.queryForObject(
"SELECT COUNT(*) FROM t_actor WHERE first_name = ?", Integer.class, "Joe");
以下示例查询一个 String:
- Imperative
- Reactive
String lastName = cqlTemplate.queryForObject(
"SELECT last_name FROM t_actor WHERE id = ?",
String.class, 1212L);
Mono<String> lastName = reactiveCqlTemplate.queryForObject(
"SELECT last_name FROM t_actor WHERE id = ?",
String.class, 1212L);
以下示例查询并填充单个领域对象:
- Imperative
- Reactive
Actor actor = cqlTemplate.queryForObject("SELECT first_name, last_name FROM t_actor WHERE id = ?",
new RowMapper<Actor>() {
public Actor mapRow(Row row, int rowNum) {
Actor actor = new Actor();
actor.setFirstName(row.getString("first_name"));
actor.setLastName(row.getString("last_name"));
return actor;
}
}, 1212L);
Mono<Actor> actor = reactiveCqlTemplate.queryForObject(
"SELECT first_name, last_name FROM t_actor WHERE id = ?",
new RowMapper<Actor>() {
public Actor mapRow(Row row, int rowNum) {
Actor actor = new Actor();
actor.setFirstName(row.getString("first_name"));
actor.setLastName(row.getString("last_name"));
return actor;
}},
1212L);
以下示例查询并填充多个领域对象:
- Imperative
- Reactive
List<Actor> actors = cqlTemplate.query(
"SELECT first_name, last_name FROM t_actor",
new RowMapper<Actor>() {
public Actor mapRow(Row row, int rowNum) {
Actor actor = new Actor();
actor.setFirstName(row.getString("first_name"));
actor.setLastName(row.getString("last_name"));
return actor;
}
});
Flux<Actor> actors = reactiveCqlTemplate.query(
"SELECT first_name, last_name FROM t_actor",
new RowMapper<Actor>() {
public Actor mapRow(Row row, int rowNum) {
Actor actor = new Actor();
actor.setFirstName(row.getString("first_name"));
actor.setLastName(row.getString("last_name"));
return actor;
}
});
如果最后两段代码片段确实存在于同一个应用中,那么就有必要消除两个 RowMapper 匿名内部类中的重复代码,把它们提取到单个类中(通常是 static 嵌套类),然后由 DAO 方法引用它。
例如,把最后一段代码片段改写成如下形式可能会更好:
- Imperative
- Reactive
List<Actor> findAllActors() {
return cqlTemplate.query("SELECT first_name, last_name FROM t_actor", ActorMapper.INSTANCE);
}
enum ActorMapper implements RowMapper<Actor> {
INSTANCE;
public Actor mapRow(Row row, int rowNum) {
Actor actor = new Actor();
actor.setFirstName(row.getString("first_name"));
actor.setLastName(row.getString("last_name"));
return actor;
}
}
Flux<Actor> findAllActors() {
return reactiveCqlTemplate.query("SELECT first_name, last_name FROM t_actor", ActorMapper.INSTANCE);
}
enum ActorMapper implements RowMapper<Actor> {
INSTANCE;
public Actor mapRow(Row row, int rowNum) {
Actor actor = new Actor();
actor.setFirstName(row.getString("first_name"));
actor.setLastName(row.getString("last_name"));
return actor;
}
}
用 CqlTemplate 执行 INSERT、UPDATE 和 DELETE
您可以使用 execute(…) 方法执行 INSERT、UPDATE 和 DELETE 操作。参数值通常以可变参数形式提供,或者也可以以对象数组形式提供。
以下示例展示了如何用 CqlTemplate 执行 INSERT 操作:
- Imperative
- Reactive
cqlTemplate.execute(
"INSERT INTO t_actor (first_name, last_name) VALUES (?, ?)",
"Leonor", "Watling");
Mono<Boolean> applied = reactiveCqlTemplate.execute(
"INSERT INTO t_actor (first_name, last_name) VALUES (?, ?)",
"Leonor", "Watling");
以下示例展示了如何用 CqlTemplate 执行 UPDATE 操作:
- Imperative
- Reactive
cqlTemplate.execute(
"UPDATE t_actor SET last_name = ? WHERE id = ?",
"Banjo", 5276L);
Mono<Boolean> applied = reactiveCqlTemplate.execute(
"UPDATE t_actor SET last_name = ? WHERE id = ?",
"Banjo", 5276L);
以下示例展示了如何用 CqlTemplate 执行 DELETE 操作:
- Imperative
- Reactive
cqlTemplate.execute(
"DELETE FROM t_actor WHERE id = ?",
5276L);
Mono<Boolean> applied = reactiveCqlTemplate.execute(
"DELETE FROM actor WHERE id = ?",
actorId);
其他 CqlTemplate 操作
您可以使用 execute(..) 方法执行任意 CQL。因此,该方法常被用于 DDL 语句。它有大量重载变体,可接受回调接口、绑定变量数组等。
以下示例展示了如何通过使用各种 API 对象(都传给 execute() 方法)来创建和删除表:
cqlTemplate.execute("CREATE TABLE test_table (id uuid primary key, event text)");
DropTableSpecification dropper = DropTableSpecification.dropTable("test_table");
String cql = DropTableCqlGenerator.toCql(dropper);
cqlTemplate.execute(cql);
控制 Cassandra 连接
应用通过 CqlSession 对象连接到 Apache Cassandra。一个 Cassandra CqlSession 维护着到各个节点的多个连接,并被设计为线程安全的长生命周期对象。通常,整个应用可以使用一个 CqlSession。
Spring 通过 SessionFactory 获取 Cassandra CqlSession。SessionFactory 是 Spring Data for Apache Cassandra 的一部分,是一个泛化的连接工厂。它让容器或框架把连接处理和路由问题对应用代码隐藏起来。
以下示例展示了如何配置一个默认的 SessionFactory:
- Imperative
- Reactive
CqlSession session = … // get a Cassandra Session
CqlTemplate template = new CqlTemplate();
template.setSessionFactory(new DefaultSessionFactory(session));
CqlSession session = … // get a Cassandra Session
ReactiveCqlTemplate template = new ReactiveCqlTemplate(new DefaultBridgedReactiveSession(session));
CqlTemplate 和其他模板 API 实现会为每次操作获取一个 CqlSession。由于会话是长生命周期的,在调用所需操作后不会关闭会话。正确释放资源的责任在于使用该会话的容器或框架。
您可以在 org.springframework.data.cassandra.core.cql.session 包中找到各种 SessionFactory 实现。
异常转换
Spring Framework 为各种数据库和映射技术提供异常转换。传统上这针对 JDBC 和 JPA。Spring Data for Apache Cassandra 通过提供 org.springframework.dao.support.PersistenceExceptionTranslator 接口的实现,将这一特性扩展到 Apache Cassandra。
映射到 Spring 一致的数据访问异常层次结构的动机,是让您能够编写可移植、描述性强的异常处理代码,而不必针对特定的 Cassandra 异常编码和处理。Spring 的所有数据访问异常都继承自 DataAccessException 类,因此您可以确信能在单个 try-catch 块中捕获所有与数据库相关的异常。
ReactiveCqlTemplate 和 ReactiveCassandraTemplate 会尽早传播异常。在响应式序列处理过程中发生的异常会作为错误信号发射。