-
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 as functions
First we need to mark a CqlSession implicit:
import net.nmoncho.helenus._
implicit val session: CqlSession = cqlSession Helenus defines several extension methods to create and prepare queries:
val hotelsByName = "SELECT * FROM hotels WHERE name = ?".toCQL.prepare[String]
// hotelsByName: internal.cql.ScalaPreparedStatement1[String, Row] = net.nmoncho.helenus.internal.cql.ScalaPreparedStatement1@66246f48
val resultSet = hotelsByName("Rotterdam Marriott").execute()
// resultSet: PagingIterable[Row] = com.datastax.oss.driver.internal.core.PagingIterableWrapper@44e96d1e
val result = resultSet.nextOption()
// result: Option[Row] = Some(
// value = com.datastax.oss.driver.internal.core.cql.DefaultRow@32fe6674
// )Let's take this step by step:
- Queries are defined as String literals with a CQL syntax.
- These have to be extended with the
.toCQLmethod. - Then by using
preparewe 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
Rows 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@25645c81
val shortHandResult = shortHandRs.nextOption()
// shortHandResult: Option[Row] = Some(
// value = com.datastax.oss.driver.internal.core.cql.DefaultRow@60ff394a
// )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 Futures):
val hotelsByNameAsync = "SELECT * FROM hotels WHERE name = ?".toCQL.prepareAsync[String]
// hotelsByNameAsync: concurrent.Future[internal.cql.ScalaPreparedStatement1[String, Row]] = Future(Success(net.nmoncho.helenus.internal.cql.ScalaPreparedStatement1@6f7129d6))
val resultAsync = for {
query <- hotelsByNameAsync
resultSet <- query.executeAsync("Rotterdam Marriott")
} yield resultSet.currPage.nextOption()
// resultAsync: concurrent.Future[Option[Row]] = Future(Success(Some(com.datastax.oss.driver.internal.core.cql.DefaultRow@2e7d2f8a)))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
prepareAsyncwe 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 Rows
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 RowMappers.
We can also page over results using Pagers.