This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Data Cassandra 4.4.0! |
CQL Template API
The CqlTemplate
class (and its reactive variant ReactiveCqlTemplate
) is the central class in the core CQL package.
It handles the creation and release of resources.
It performs the basic tasks of the core CQL workflow, such as statement creation and execution, and leaves application code to provide CQL and extract results.
The CqlTemplate
class executes CQL queries and update statements, performs iteration over ResultSet
instances and extraction of returned parameter values.
It also catches CQL exceptions and translates them to the generic, more informative, exception hierarchy defined in the org.springframework.dao
package.
When you use the CqlTemplate
for your code, you need only implement callback interfaces, which have a clearly defined contract.
Given a CqlSession
, the PreparedStatementCreator
callback interface creates a prepared statement with the provided CQL and any necessary parameter arguments.
The RowCallbackHandler
interface extracts values from each row of a ResultSet
.
The CqlTemplate
can be used within a DAO implementation through direct instantiation with a SessionFactory
reference or be configured in the Spring container and given to DAOs as a bean reference. CqlTemplate
is a foundational building block for CassandraTemplate
.
All CQL issued by this class is logged at the DEBUG
level under the category corresponding to the fully-qualified class name of the template instance (typically CqlTemplate
, but it may be different if you use a custom subclass of the CqlTemplate
class).
You can control fetch size, consistency level, and retry policy defaults by configuring these parameters on the CQL API instances:
CqlTemplate
, AsyncCqlTemplate
, and ReactiveCqlTemplate
.
Defaults apply if the particular query option is not set.
CqlTemplate comes in different execution model flavors.
The basic CqlTemplate uses a blocking execution model.
You can use AsyncCqlTemplate for asynchronous execution and synchronization with ListenableFuture instances or
ReactiveCqlTemplate for reactive execution.
|
Examples of CqlTemplate
Class Usage
This section provides some examples of the CqlTemplate
class in action.
These examples are not an exhaustive list of all functionality exposed by the CqlTemplate
.
See the Javadoc for that.
Querying (SELECT) with CqlTemplate
The following query gets the number of rows in a table:
-
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);
The following query uses a bind variable:
-
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");
The following example queries for a 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);
The following example queries and populates a single domain object:
-
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);
The following example queries and populates multiple domain objects:
-
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;
}
});
If the last two snippets of code actually existed in the same application, it would make sense to remove the duplication present in the two RowMapper
anonymous inner classes and extract them out into a single class (typically a static
nested class) that can then be referenced by DAO methods.
For example, it might be better to write the last code snippet as follows:
-
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;
}
}
INSERT
, UPDATE
, and DELETE
with CqlTemplate
You can use the execute(…)
method to perform INSERT
, UPDATE
, and DELETE
operations.
Parameter values are usually provided as variable arguments or, alternatively, as an object array.
The following example shows how to perform an INSERT
operation with CqlTemplate
:
-
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");
The following example shows how to perform an UPDATE
operation with CqlTemplate
:
-
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);
The following example shows how to perform an DELETE
operation with CqlTemplate
:
-
Imperative
-
Reactive
cqlTemplate.execute(
"DELETE FROM t_actor WHERE id = ?",
5276L);
Mono<Boolean> applied = reactiveCqlTemplate.execute(
"DELETE FROM actor WHERE id = ?",
actorId);
Other CqlTemplate
operations
You can use the execute(..)
method to execute any arbitrary CQL.
As a result, the method is often used for DDL statements.
It is heavily overloaded with variants that take callback interfaces, bind variable arrays, and so on.
The following example shows how to create and drop a table by using different API objects that are all passed to the execute()
methods:
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);
Controlling Cassandra Connections
Applications connect to Apache Cassandra by using CqlSession
objects.
A Cassandra CqlSession
keeps track of multiple connections to the individual nodes and is designed to be a thread-safe, long-lived object.
Usually, you can use a single CqlSession
for the whole application.
Spring acquires a Cassandra CqlSession
through a SessionFactory
. SessionFactory
is part of Spring Data for Apache Cassandra and is a generalized connection factory.
It lets the container or framework hide connection handling and routing issues from the application code.
The following example shows how to configure a default 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
and other Template API implementations obtain a CqlSession
for each operation.
Due to their long-lived nature, sessions are not closed after invoking the desired operation.
Responsibility for proper resource disposal lies with the container or framework that uses the session.
You can find various SessionFactory
implementations within the org.springframework.data.cassandra.core.cql.session
package.
Exception Translation
The Spring Framework provides exception translation for a wide variety of database and mapping technologies.
This has traditionally been for JDBC and JPA.
Spring Data for Apache Cassandra extends this feature to Apache Cassandra by providing an implementation of the org.springframework.dao.support.PersistenceExceptionTranslator
interface.
The motivation behind mapping to Spring’s consistent data access exception hierarchy
is to let you write portable and descriptive exception handling code without resorting to coding against and handling specific Cassandra exceptions.
All of Spring’s data access exceptions are inherited from the
DataAccessException
class, so you can be sure that you can catch all database-related exceptions within a single try-catch block.
ReactiveCqlTemplate
and ReactiveCassandraTemplate
propagate exceptions as early as possible.
Exceptions that occur during the processing of the reactive sequence are emitted as error signals.