11## Prepared statements
22
3- When Cassandra executes a query, the first thing it does is parse the
4- query string to an internal representation. If the same query is used
5- often, you can use a prepared statement, which allows Cassandra to cache
6- that representation, and save time and resources for subsequent
7- executions. Prepared statements are typically parameterized, using
8- different values for each execution:
3+ Use [ PreparedStatement] for queries that are executed multiple times in your application:
94
105``` java
116PreparedStatement prepared = session. prepare(
127 " insert into product (sku, description) values (?, ?)" );
138
14- BoundStatement bound;
15-
16- bound = prepared. bind(" 234827" , " Mouse" );
9+ BoundStatement bound = prepared. bind(" 234827" , " Mouse" );
1710session. execute(bound);
1811
19- bound = prepared. bind(" 987274" , " Keyboard" );
20- session. execute(bound);
12+ session. execute(prepared. bind(" 987274" , " Keyboard" ));
2113```
2214
23- Statements should be be prepared only once. If you call ` prepare `
24- multiple times with the same query string, the driver will log a
25- warning. So your application should cache the ` PreparedStatement ` object
26- once it's been created (this can be as simple as storing it as a field
27- in a DAO).
15+ When you prepare the statement, Cassandra will parse the query string, cache the result and return a unique identifier
16+ (the ` PreparedStatement ` object keeps an internal reference to that identifier):
17+
18+ ``` ditaa
19+ client driver Cassandra
20+ --+------------------------+----------------+------
21+ | | |
22+ | session.prepare(query) | |
23+ |----------------------->| |
24+ | | PREPARE(query) |
25+ | |--------------->|
26+ | | |
27+ | | |
28+ | | | - compute id
29+ | | | - parse query string
30+ | | | - cache (id, parsed)
31+ | | |
32+ | | PREPARED(id) |
33+ | |<---------------|
34+ | PreparedStatement(id) | |
35+ |<-----------------------| |
36+ ```
37+
38+ When you bind and execute a prepared statement, the driver will only send the identifier, which allows Cassandra to
39+ skip the parsing phase:
40+
41+ ``` ditaa
42+ client driver Cassandra
43+ --+---------------------------------+---------------------+------
44+ | | |
45+ | session.execute(BoundStatement) | |
46+ |-------------------------------->| |
47+ | | EXECUTE(id, values) |
48+ | |-------------------->|
49+ | | |
50+ | | |
51+ | | | - get cache(id)
52+ | | | - execute query
53+ | | |
54+ | | ROWS |
55+ | |<--------------------|
56+ | | |
57+ |<--------------------------------| |
58+ ```
59+
60+
61+ You should prepare only once, and cache the ` PreparedStatement ` in your application (it is thread-safe). If you call
62+ ` prepare ` multiple times with the same query string, the driver will log a warning.
63+
64+ If you execute a query only once, a prepared statement is inefficient because it requires two roundtrips. Consider a
65+ [ simple statement] ( ../simple/ ) instead.
2866
2967### Parameters and binding
3068
@@ -36,9 +74,8 @@ ps1 = session.prepare("insert into product (sku, description) values (?, ?)");
3674ps2 = session. prepare(" insert into product (sku, description) values (:s, :d)" );
3775```
3876
39- To turn the statement into its executable form, you need to * bind* it
40- and provide values for the parameters. As shown previously, there is a
41- shorthand form to do it all in one call:
77+ To turn the statement into its executable form, you need to * bind* it to create a [ BoundStatement] . As shown previously,
78+ there is a shorthand to provide the parameters in the same call:
4279
4380``` java
4481BoundStatement bound = ps1. bind(" 324378" , " LCD screen" );
@@ -59,8 +96,9 @@ BoundStatement bound = ps2.bind()
5996 .setString(" d" , " LCD screen" );
6097```
6198
62- If you don't set a parameter, it is sent as ` null ` (note that this
63- behavior changes in the 2.1 branch of the driver).
99+ You must set all parameters. If you fail to do so, the driver will throw an error when executing the statement. If you
100+ want to send a ` NULL ` value, set it explicitly to ` null ` (use ` setToNull ` for Java primitives). Note that this is a new
101+ behavior in 2.1, branch 2.0 of the driver assumed unset values were null.
64102
65103You can use named setters even if the query uses anonymous parameters;
66104Cassandra will name the parameters after the column they apply to:
@@ -79,21 +117,19 @@ A bound statement also has getters to retrieve the values. Note that
79117this has a small performance overhead since values are stored in their
80118serialized form.
81119
82- ### How the driver handles prepared statements
120+ ` BoundStatement ` is ** not thread-safe** . You can reuse an instance multiple times with different parameters, but only
121+ from a single thread, and only if you use the synchronous API ([ Session#execute] [ execute ] is fine,
122+ [ Session#executeAsync] [ executeAsync ] is not).
123+ Also, make sure you don't accidentally reuse parameters from previous executions.
83124
84- When the driver prepares a statement, it sends the query string to
85- Cassandra, which caches the statement and returns an identifier. Later,
86- when the driver needs to execute the statement, it just sends the
87- identifier and parameter values. Note that the identifier is
88- deterministic, so it will always be the same for all nodes (it's a
89- actually a hash of the query string).
125+ ### Preparing on multiple nodes
90126
91- Prepared statements are not replicated across the cluster. It is the
127+ Cassandra does not replicate prepared statements across the cluster. It is the
92128driver's responsibility to ensure that each node's cache is up to
93129date. It uses a number of strategies to achieve this:
94130
951311 . When a statement is initially prepared, it is first sent to a single
96- node in the cluster (this prevents from hitting all nodes in case
132+ node in the cluster (this avoids hitting all nodes in case
97133 the query string is wrong). Once that node replies successfully, the
98134 driver re-prepares on all remaining nodes:
99135
@@ -119,6 +155,9 @@ date. It uses a number of strategies to achieve this:
119155 |<-----------------------| | | |
120156 ```
121157
158+ The prepared statement identifier is deterministic (it's a hash of the query string), so it is the same
159+ for all nodes.
160+
1221612. if a node crashes, it loses all of its prepared statements. So the
123162 driver keeps a client-side cache; anytime a node is marked back up,
124163 the driver re-prepares all statements on it;
@@ -168,5 +207,9 @@ Changing the driver's defaults should be done with care and only in
168207specific situations; read each method's Javadoc for detailed
169208explanations.
170209
210+ [PreparedStatement]: http://docs.datastax.com/en/drivers/java/2.1/com/datastax/driver/core/PreparedStatement.html
211+ [BoundStatement]: http://docs.datastax.com/en/drivers/java/2.1/com/datastax/driver/core/BoundStatement.html
171212[setPrepareOnAllHosts]: http://docs.datastax.com/en/drivers/java/2.1/com/datastax/driver/core/QueryOptions.html#setPrepareOnAllHosts-boolean-
172213[setReprepareOnUp]: http://docs.datastax.com/en/drivers/java/2.1/com/datastax/driver/core/QueryOptions.html#setReprepareOnUp-boolean-
214+ [execute]: http://docs.datastax.com/en/drivers/java/3.0/com/datastax/driver/core/Session.html#execute-com.datastax.driver.core.Statement-
215+ [executeAsync]: http://docs.datastax.com/en/drivers/java/3.0/com/datastax/driver/core/Session.html#executeAsync-com.datastax.driver.core.Statement-
0 commit comments