-
Notifications
You must be signed in to change notification settings - Fork 1
Queries and Statements as Functions
Let's see how we can query Cassandra using PreparedStatement
s as functions
First we need to mark a CqlSession implicit:
import net.nmoncho.helenus.*
given CqlSession = cqlSession
Helenus defines several extension methods to create and prepare queries:
val hotelsByName = "SELECT * FROM hotels WHERE name = ?".toCQL.prepare[String]
// hotelsByName: ScalaPreparedStatement1[String, Row] = net.nmoncho.helenus.internal.cql.ScalaPreparedStatement1@2e63d4f8
val resultSet = hotelsByName("Rotterdam Marriott").execute()
// resultSet: PagingIterable[Row] = com.datastax.oss.driver.internal.core.PagingIterableWrapper@103b3aad
val result = resultSet.nextOption()
// result: Option[Row] = Some(
// value = com.datastax.oss.driver.internal.core.cql.DefaultRow@41a6e183
// )
Let's take this step by step:
- Queries are defined as String literals with a CQL syntax.
- These have to be extended with the
.toCQL
method. - Then by using
prepare
we define how many parameters the statement has and what types does those parameters have. Here we obtain aScalaPreparedStatement
, a similar construction to PreparedStatement. - When a query is prepared, we can treat it as function that will produce BoundStatement when all parameters are provided.
- We can decide whenever we want to execute these
BoundStatements
, which returns aResultSet
. - Finally, we also have extension methods on ResultSet to extract
Row
s from them.
There is also a short-hand syntax for executing a query:
val shortHandRs = hotelsByName.execute("Rotterdam Marriott")
// shortHandRs: PagingIterable[Row] = com.datastax.oss.driver.internal.core.PagingIterableWrapper@4e05ea7f
val shortHandResult = shortHandRs.nextOption()
// shortHandResult: Option[Row] = Some(
// value = com.datastax.oss.driver.internal.core.cql.DefaultRow@7e713353
// )
After a statement is prepared, Helenus will check at runtime if the provided parameters types match what the database is expecting. If these don't match, a warning will be logged.
We can also define and execute statement asynchronously (ie. with Future
s):
val hotelsByNameAsync = "SELECT * FROM hotels WHERE name = ?".toCQL.prepareAsync[String]
// hotelsByNameAsync: Future[ScalaPreparedStatement1[String, Row]] = Future(Success(net.nmoncho.helenus.internal.cql.ScalaPreparedStatement1@37569062))
val resultAsync = for {
query <- hotelsByNameAsync
resultSet <- query.executeAsync("Rotterdam Marriott")
} yield resultSet.currPage.nextOption()
// resultAsync: Future[Option[Row]] = Future(Success(Some(com.datastax.oss.driver.internal.core.cql.DefaultRow@418ac5f2)))
Let's take this step by step:
- Queries are again defined as String literals with a CQL syntax, and are extended with
.toCQL
. - Then by using
prepareAsync
we define how many and what types does those, parameters have. This returns aFuture[ScalaPreparedStatement]
. - When a query is prepared, we can execute it whenever we see fit using the short-hand method
executeAsync
.
Defining and executing queries is one part of the story. The other part is how we make sense
of the Row
s
we get out of these queries.
We can iterate results using some extension methods, and we can map
rows to a more meaningful result using RowMapper
s.
We can also page over results using Pager
s.