blob: 5ca4cd7872f98cf92b87a8af5bfa88d882765c5b [file] [log] [blame] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
## Core driver
The core module handles cluster connectivity and request execution. It is published under the
following coordinates:
```xml
<dependency>
<groupId>org.apache.cassandra</groupId>
<artifactId>java-driver-core</artifactId>
<version>${driver.version}</version>
</dependency>
```
(For more details on setting up your build tool, see the [integration](integration/) page.)
### Quick start
Here's a short program that connects to Cassandra and executes a query:
```java
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.*;
try (CqlSession session = CqlSession.builder().build()) { // (1)
ResultSet rs = session.execute("select release_version from system.local"); // (2)
Row row = rs.one();
System.out.println(row.getString("release_version")); // (3)
}
```
1. [CqlSession] is the main entry point of the driver. It holds the known state of the actual
Cassandra cluster, and is what you use to execute queries. It is thread-safe, you should create a
single instance (per target Cassandra cluster), and share it throughout your application;
2. we use `execute` to send a query to Cassandra. This returns a [ResultSet], which is an iterable
of [Row] objects. On the next line, we extract the first row (which is the only one in this case);
3. we extract the value of the first (and only) column from the row.
Always close the `CqlSession` once you're done with it, in order to free underlying resources (TCP
connections, thread pools...). In this simple example, we can use a try-with-resources block because
`CqlSession` implements `java.lang.AutoCloseable`; in a real application, you'll probably call one
of the close methods (`close`, `closeAsync`, `forceCloseAsync`) explicitly.
This example uses the synchronous API. Most methods have asynchronous equivalents (look for `*Async`
variants that return a `CompletionStage`).
### Setting up the driver
#### [CqlSession]
[CqlSession#builder()] provides a fluent API to create an instance programmatically. Most of the
customization is done through the driver configuration (refer to the
[corresponding section](configuration/) of this manual for full details).
We recommend that you take a look at the [reference configuration](configuration/reference/) for the
list of available options, and cross-reference with the sub-sections in this manual for more
explanations.
By default, `CqlSession.builder().build()` fails immediately if the cluster is not available. If you
want to retry instead, you can set the [reconnect-on-init](reconnection/#at-init-time) option in the
configuration.
##### Contact points
If you don't specify any contact point, the driver defaults to `127.0.0.1:9042`:
```java
CqlSession session = CqlSession.builder().build();
```
This is fine for a quick start on a developer workstation, but you'll quickly want to provide
specific addresses. There are two ways to do this:
* via [SessionBuilder.addContactPoint()] or [SessionBuilder.addContactPoints()];
* in the [configuration](configuration/) via the `basic.contact-points` option.
As soon as there are explicit contact points, you also need to provide the name of the local
datacenter. All contact points must belong to it (as reported in their system tables:
`system.local.data_center` and `system.peers.data_center`). Again this can be specified either:
* via [SessionBuilder.withLocalDatacenter()];
* in the configuration via the `basic.load-balancing-policy.local-datacenter` option.
Here is a full programmatic example:
```java
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("1.2.3.4", 9042))
.addContactPoint(new InetSocketAddress("5.6.7.8", 9042))
.withLocalDatacenter("datacenter1")
.build();
```
And a full configuration example:
```
// Add `application.conf` to your classpath with the following contents:
datastax-java-driver {
basic {
contact-points = [ "1.2.3.4:9042", "5.6.7.8:9042" ]
load-balancing-policy.local-datacenter = datacenter1
}
}
```
For more details about the local datacenter, refer to the [load balancing
policy](load_balancing/#local-only) section.
##### Keyspace
By default, a session isn't tied to any specific keyspace. You'll need to prefix table names in your
queries:
```java
session.execute("SELECT * FROM my_keyspace.my_table WHERE id = 1");
```
You can also specify a keyspace at construction time, either through the
[configuration](configuration/):
```
datastax-java-driver {
basic.session-keyspace = my_keyspace
}
```
Or with the builder:
```java
CqlSession session = CqlSession.builder()
.withKeyspace(CqlIdentifier.fromCql("my_keyspace"))
.build();
```
That keyspace will be used as the default when table names are not qualified:
```java
session.execute("SELECT * FROM my_table WHERE id = 1");
session.execute("SELECT * FROM other_keyspace.other_table WHERE id = 1");
```
You might be tempted to open a separate session for each keyspace used in your application; however,
connection pools are created at the session level, so each new session will consume additional
system resources:
```java
// Anti-pattern: creating two sessions doubles the number of TCP connections opened by the driver
CqlSession session1 = CqlSession.builder().withKeyspace(CqlIdentifier.fromCql("ks1")).build();
CqlSession session2 = CqlSession.builder().withKeyspace(CqlIdentifier.fromCql("ks2")).build();
```
If you issue a `USE` statement, it will change the default keyspace on that session:
```java
CqlSession session = CqlSession.builder().build();
// No default keyspace set, need to prefix:
session.execute("SELECT * FROM my_keyspace.my_table WHERE id = 1");
session.execute("USE my_keyspace");
// Now the keyspace is set, unqualified query works:
session.execute("SELECT * FROM my_table WHERE id = 1");
```
Be very careful though: switching the keyspace at runtime is inherently thread-unsafe, so if the
session is shared by multiple threads (and is usually is), it could easily cause unexpected query
failures.
Finally, if you're connecting to Cassandra 4 or above, you can specify the keyspace independently
for each request:
```java
CqlSession session = CqlSession.builder().build();
session.execute(
SimpleStatement.newInstance("SELECT * FROM my_table WHERE id = 1")
.setKeyspace(CqlIdentifier.fromCql("my_keyspace")));
```
### Running queries
You run queries with the session's `execute*` methods:
```java
ResultSet rs = session.execute("SELECT release_version FROM system.local");
```
As shown here, the simplest form is to pass a query string directly. You can also pass a
[Statement](statements/) instance.
#### Processing rows
Executing a query produces a [ResultSet], which is an iterable of [Row]. The basic way to process
all rows is to use Java's for-each loop:
```java
for (Row row : rs) {
// process the row
}
```
This will return **all results** without limit (even though the driver might use multiple queries in
the background). To handle large result sets, you might want to use a `LIMIT` clause in your CQL
query, or use one of the techniques described in the [paging](paging/) documentation.
When you know that there is only one row (or are only interested in the first one), the driver
provides a convenience method:
```java
Row row = rs.one();
```
#### Reading columns
[Row] provides getters to extract column values; they can be either positional or named:
```java
Row row = session.execute("SELECT first_name, last_name FROM users WHERE id = 1").one();
// The two are equivalent:
String firstName = row.getString(0);
String firstName = row.getString(CqlIdentifier.fromCql("first_name"));
```
[CqlIdentifier] is a string wrapper that deals with case-sensitivity. If you don't want to create an
instance for each getter call, the driver also provides convenience methods that take a raw string:
```java
String firstName = row.getString("first_name");
```
See [AccessibleByName] for an explanation of the conversion rules.
##### CQL to Java type mapping
| CQL3 data type | Getter name | Java type | See also |
|--------------------|----------------|----------------------|-----------------------------------|
| ascii | getString | java.lang.String | |
| bigint | getLong | long | |
| blob | getByteBuffer | java.nio.ByteBuffer | |
| boolean | getBoolean | boolean | |
| counter | getLong | long | |
| date | getLocalDate | java.time.LocalDate | [Temporal types](temporal_types/) |
| decimal | getBigDecimal | java.math.BigDecimal | |
| double | getDouble | double | |
| duration | getCqlDuration | [CqlDuration] | [Temporal types](temporal_types/) |
| float | getFloat | float | |
| inet | getInetAddress | java.net.InetAddress | |
| int | getInt | int | |
| list | getList | java.util.List<T> | |
| map | getMap | java.util.Map<K, V> | |
| set | getSet | java.util.Set<T> | |
| smallint | getShort | short | |
| text | getString | java.lang.String | |
| time | getLocalTime | java.time.LocalTime | [Temporal types](temporal_types/) |
| timestamp | getInstant | java.time.Instant | [Temporal types](temporal_types/) |
| timeuuid | getUuid | java.util.UUID | |
| tinyint | getByte | byte | |
| tuple | getTupleValue | [TupleValue] | [Tuples](tuples/) |
| user-defined types | getUDTValue | [UDTValue] | [User-defined types](udts/) |
| uuid | getUuid | java.util.UUID | |
| varchar | getString | java.lang.String | |
| varint | getBigInteger | java.math.BigInteger | |
| vector | getVector | [CqlVector] | [Custom Codecs](custom_codecs/) |
Sometimes the driver has to infer a CQL type from a Java type (for example when handling the values
of [simple statements](statements/simple/)); for those that have multiple CQL equivalents, it makes
the following choices:
* `java.lang.String`: `text`
* `long`: `bigint`
* `java.util.UUID`: `uuid`
In addition to these default mappings, you can register your own types with
[custom codecs](custom_codecs/).
##### Primitive types
For performance reasons, the driver uses primitive Java types wherever possible (`boolean`,
`int`...); the CQL value `NULL` is encoded as the type's default value (`false`, `0`...), which can
be ambiguous. To distinguish `NULL` from actual values, use `isNull`:
```java
Integer age = row.isNull("age") ? null : row.getInt("age");
```
##### Collection types
To ensure type safety, collection getters are generic. You need to provide type parameters matching
your CQL type when calling the methods:
```java
// Assuming given_names is a list<text>:
List<String> givenNames = row.getList("given_names", String.class);
```
For nested collections, element types are generic and cannot be expressed as Java `Class` instances.
Use [GenericType] instead:
```java
// Assuming teams is a set<list<text>>:
GenericType<Set<List<String>>> listOfStrings = new GenericType<Set<List<String>>>() {};
Set<List<String>> teams = row.get("teams", listOfStrings);
```
Since generic types are anonymous inner classes, it's recommended to store them as constants in a
utility class instead of re-creating them each time.
##### Row metadata
[ResultSet] and [Row] expose an API to explore the column metadata at runtime:
```java
for (ColumnDefinitions.Definition definition : row.getColumnDefinitions()) {
System.out.printf("Column %s has type %s%n",
definition.getName(),
definition.getType());
}
```
[CqlSession]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/CqlSession.html
[CqlSession#builder()]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/CqlSession.html#builder--
[ResultSet]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/cql/ResultSet.html
[Row]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/cql/Row.html
[CqlIdentifier]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/CqlIdentifier.html
[AccessibleByName]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/data/AccessibleByName.html
[GenericType]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/type/reflect/GenericType.html
[CqlDuration]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/data/CqlDuration.html
[CqlVector]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/data/CqlVector.html
[TupleValue]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/data/TupleValue.html
[UdtValue]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/data/UdtValue.html
[SessionBuilder.addContactPoint()]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/session/SessionBuilder.html#addContactPoint-java.net.InetSocketAddress-
[SessionBuilder.addContactPoints()]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/session/SessionBuilder.html#addContactPoints-java.util.Collection-
[SessionBuilder.withLocalDatacenter()]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/session/SessionBuilder.html#withLocalDatacenter-java.lang.String-
[CASSANDRA-10145]: https://issues.apache.org/jira/browse/CASSANDRA-10145