blob: a6630a363b455e2ba56d08f65338198db5dd6d3c [file] [log] [blame] [view]
## Per-query keyspace
### Quick overview
Specify the keyspace separately instead of hardcoding it in the query string.
* Cassandra 4+ / DSE 6+.
* only works with simple statements.
-----
Sometimes it is convenient to send the keyspace separately from the query string, and without
switching the whole session to that keyspace either. For example, you might have a multi-tenant
setup where identical requests are executed against different keyspaces.
**This feature is only available with Cassandra 4.0 or above** ([CASSANDRA-10145]). Make sure you
are using [native protocol](../../native_protocol/) v5 or above to connect.
If you try against an older version, you will get an error:
```
Exception in thread "main" java.lang.IllegalArgumentException: Can't use per-request keyspace with protocol V4
```
*Note: at the time of writing, Cassandra 4 is not released yet. If you want to test those examples
against the development version, keep in mind that native protocol v5 is still in beta, so you'll
need to force it in the configuration: `datastax-java-driver.protocol.version = V5`*.
### Basic usage
To use a per-query keyspace, set it on your statement instance:
```java
CqlSession session = CqlSession.builder().build();
CqlIdentifier keyspace = CqlIdentifier.fromCql("test");
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = 1").setKeyspace(keyspace);
session.execute(statement);
```
You can do this on [simple](../simple/), [prepared](../prepared) or [batch](../batch/) statements.
If the session is connected to another keyspace, the per-query keyspace takes precedence:
```java
CqlIdentifier keyspace1 = CqlIdentifier.fromCql("test1");
CqlIdentifier keyspace2 = CqlIdentifier.fromCql("test2");
CqlSession session = CqlSession.builder().withKeyspace(keyspace1).build();
// Will query test2.foo:
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = 1").setKeyspace(keyspace2);
session.execute(statement);
```
On the other hand, if a keyspace is hard-coded in the query, it takes precedence over the per-query
keyspace:
```java
// Will query test1.foo:
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM test1.foo WHERE k = 1").setKeyspace(keyspace2);
```
### Bound statements
Bound statements can't have a per-query keyspace; they only inherit the one that was set on the
prepared statement:
```java
CqlIdentifier keyspace = CqlIdentifier.fromCql("test");
PreparedStatement pst =
session.prepare(
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = ?").setKeyspace(keyspace));
// Will query test.foo:
BoundStatement bs = pst.bind(1);
```
The rationale is that prepared statements hold metadata about the target table; if Cassandra allowed
execution against different keyspaces, it would be under the assumption that all tables have the
same exact schema, which could create issues if this turned out not to be true at runtime.
Therefore you'll have to prepare against every target keyspace. A good strategy is to do this lazily
with a cache. Here is a simple example using Guava:
```java
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
LoadingCache<CqlIdentifier, PreparedStatement> cache =
CacheBuilder.newBuilder()
.build(
new CacheLoader<CqlIdentifier, PreparedStatement>() {
@Override
public PreparedStatement load(CqlIdentifier keyspace) throws Exception {
return session.prepare(
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = ?")
.setKeyspace(keyspace));
}
});
CqlIdentifier keyspace = CqlIdentifier.fromCql("test");
BoundStatement bs = cache.get(keyspace).bind(1);
```
### Relation to the routing keyspace
Statements have another keyspace-related method: `Statement.setRoutingKeyspace()`. However, the
routing keyspace is only used for [token-aware routing], as a hint to help the driver send requests
to the best replica. It does not affect the query string itself.
If you are using a per-query keyspace, the routing keyspace becomes obsolete: the driver will use
the per-query keyspace as the routing keyspace.
```java
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = 1")
.setKeyspace(keyspace)
.setRoutingKeyspace(keyspace); // NOT NEEDED: will be ignored
```
At some point in the future, when Cassandra 4 becomes prevalent and using a per-query keyspace is
the norm, we'll probably deprecate `setRoutingKeyspace()`.
[token-aware routing]: https://docs.datastax.com/en/drivers/java/4.8/com/datastax/oss/driver/api/core/session/Request.html#getRoutingKey--
[CASSANDRA-10145]: https://issues.apache.org/jira/browse/CASSANDRA-10145