预编译语句
多次执行的 CQL 语句可以被预编译并存储在 PreparedStatement 对象中,以提升查询性能。
驱动程序和 Cassandra 都会维护一个 PreparedStatement 查询与其元数据之间的映射关系。
你可以通过以下抽象来使用预编译语句:
-
CassandraTemplate、AsyncCassandraTemplate或ReactiveCassandraTemplate,通过启用预编译语句实现 -
Cassandra 仓库,因为它们构建于模板 API 之上
使用CqlTemplate
CqlTemplate 类(及其异步和响应式变体)提供了多种方法,可接受静态 CQL、Statement 对象以及 PreparedStatementCreator。
接受静态 CQL 且不带额外参数的方法通常会直接运行该 CQL 语句,不做进一步处理。
接受静态 CQL 并配合参数数组的方法(例如 execute(String cql, Object… args) 和 queryForRows(String cql, Object… args))则使用预编译语句(prepared statements)。
在内部,这些方法会创建 PreparedStatementCreator 和 PreparedStatementBinder 对象,用于准备语句并在稍后将值绑定到该语句以执行。
Spring Data Cassandra 通常对预编译语句使用基于索引的参数绑定。
自 Cassandra Driver 4 版本起,预编译语句(prepared statements)已在驱动程序层面进行缓存,从而无需在应用程序中手动跟踪预编译语句。
以下示例展示了如何使用带参数的预编译语句执行查询:
-
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);
在需要对语句准备和参数绑定进行更多控制的情况下(例如,使用命名绑定参数),你可以通过调用带有 PreparedStatementCreator 和 PreparedStatementBinder 参数的查询方法,完全控制预编译语句的创建和参数绑定:
-
Imperative
-
Reactive
List<String> lastNames = cqlTemplate.query(
session -> session.prepare("SELECT last_name FROM t_actor WHERE id = ?"),
ps -> ps.bind(1212L),
(row, rowNum) -> row.getString(0));
Flux<String> lastNames = reactiveCqlTemplate.query(
session -> session.prepare("SELECT last_name FROM t_actor WHERE id = ?"),
ps -> ps.bind(1212L),
(row, rowNum) -> row.getString(0));
Spring Data Cassandra 在 cql 包中提供了支持该模式的类:
-
SimplePreparedStatementCreator- 用于创建预编译语句的工具类。 -
ArgumentPreparedStatementBinder- 用于将参数绑定到预编译语句(prepared statement)的工具类。
使用CassandraTemplate
CassandraTemplate 类构建于 CqlTemplate 之上,提供了更高层次的抽象。
可以通过调用 CassandraTemplate 或 setUsePreparedStatements(false) 直接在 setUsePreparedStatements(true)(及其异步和响应式变体)上控制是否使用预编译语句。
请注意,CassandraTemplate 默认启用预编译语句。
以下示例展示了生成和接受 CQL 的方法的使用:
-
Imperative
-
Reactive
template.setUsePreparedStatements(true);
Actor actorByQuery = template.selectOne(query(where("id").is(42)), Actor.class);
Actor actorByStatement = template.selectOne(
SimpleStatement.newInstance("SELECT id, name FROM actor WHERE id = ?", 42),
Actor.class);
template.setUsePreparedStatements(true);
Mono<Actor> actorByQuery = template.selectOne(query(where("id").is(42)), Actor.class);
Mono<Actor> actorByStatement = template.selectOne(
SimpleStatement.newInstance("SELECT id, name FROM actor WHERE id = ?", 42),
Actor.class);
调用诸如 select(Query, Class<T>) 或 update(Query, Update, Class<T>) 等绑定实体的方法时,会自行构建 CQL 语句以执行预期的操作。
某些 CassandraTemplate 方法(例如 select(Statement<?>, Class<T>))在其 API 中也接受 CQL Statement 对象。
在调用接受 Statement 参数的方法时,如果传入的是 SimpleStatement 对象,则可以参与预编译语句(prepared statements)的执行。
模板 API 会提取查询字符串和参数(包括位置参数和命名参数),并使用这些信息来预编译、绑定并执行该语句。
非 SimpleStatement 对象无法用于预编译语句。
缓存预编译语句
从 Cassandra 驱动程序 4.0 版本开始,预编译语句(prepared statements)由 CqlSession 缓存,因此重复准备相同的字符串是安全的。
在早期版本中,需要在驱动程序外部对预编译语句进行缓存。
更多详情请参阅驱动程序关于预编译语句的文档。