预编译语句
多次执行的 CQL 语句可以预编译并存储在 PreparedStatement 对象中,以提升查询性能。驱动和 Cassandra 都会维护 PreparedStatement 查询与其元数据的映射。您可以通过以下抽象使用预编译语句:
-
通过选择 API 使用 CqlTemplate、AsyncCqlTemplate 或 ReactiveCqlTemplate
-
通过启用预编译语句使用 CassandraTemplate、AsyncCassandraTemplate 或 ReactiveCassandraTemplate
-
使用 Cassandra 仓库,因为它们构建在模板 API 之上
使用 CqlTemplate
CqlTemplate 类(及其异步和响应式变体)提供了多种方法,可接受静态 CQL、Statement 对象和 PreparedStatementCreator。接受静态 CQL 且不带额外参数的方法通常会按原样运行 CQL 语句,不做进一步处理。接受静态 CQL 并配合参数数组的方法(如 execute(String cql, Object… args) 和 queryForRows(String cql, Object… args))会使用预编译语句。在内部,这些方法会创建 PreparedStatementCreator 和 PreparedStatementBinder 对象来预编译语句,之后再把值绑定到语句上以运行它。Spring Data Cassandra 通常对预编译语句使用基于索引的参数绑定。
自 Cassandra Driver 4 起,预编译语句在驱动层缓存,因此无需在应用中跟踪预编译语句。
以下示例展示了如何使用带参数的预编译语句发起查询:
- 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——用于将参数绑定到预编译语句的工具类。
使用 CassandraTemplate
CassandraTemplate 类构建在 CqlTemplate 之上,以提供更高层次的抽象。可以直接在 CassandraTemplate(及其异步和响应式变体)上通过调用 setUsePreparedStatements(false) 或 setUsePreparedStatements(true) 来控制是否使用预编译语句。请注意,CassandraTemplate 默认启用预编译语句。
以下示例展示了生成 CQL 的方法和接受 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>))也接受 CQL Statement 对象作为其 API 的一部分。
调用接受 Statement 的方法时传入 SimpleStatement 对象,即可参与预编译语句。模板 API 会提取查询字符串和参数(位置参数和命名参数),并用它们来预编译、绑定和运行语句。非 SimpleStatement 对象不能用于预编译语句。
预编译语句缓存
自 Cassandra driver 4.0 起,预编译语句由 CqlSession 缓存缓存,因此可以放心地重复预编译同一个字符串。早期版本需要在驱动之外缓存预编译语句。另请参阅驱动文档中关于预编译语句的更多参考。