DataStax Java Driver: 2.2.0-rc1 released!
The Java driver team is pleased to announce the release of version 2.2.0-rc1, which brings parity with Cassandra 2.2. Here’s what the new Cassandra features change for driver users:
- New CQL data types
- New metadata for user-defined functions
- Unbound variables
- Query warnings
New CQL data types
Four new types were introduced:
These two integer types match Java’s
byte, so their usage should be quite straightforward:
session.execute("CREATE TABLE IF NOT EXISTS small_ints(s smallint PRIMARY KEY, t tinyint)"); PreparedStatement pst = session.prepare("INSERT INTO small_ints (s, t) VALUES (:s, :t)"); session.execute(pst.bind(Short.MIN_VALUE, Byte.MAX_VALUE)); Row row = session.execute("SELECT * FROM small_ints").one(); short s = row.getShort("s"); byte t = row.getByte("t");
There is one minor catch: Java’s integer literals default to
int, which the driver serializes as CQL
INTs. So the following will fail:
session.execute(pst.bind(1, 1)); // InvalidTypeException: Invalid type for value 0 of CQL type smallint, // expecting class java.lang.Short but class java.lang.Integer provided
The workaround is simply to coerce your arguments to the correct type:
In previous versions, Cassandra only had
TIMESTAMP to represent date and time. The two new types add support for date-only and time-only values:
session.execute("CREATE TABLE IF NOT EXISTS dates(ts timestamp PRIMARY KEY, d date, t time)"); session.execute("INSERT INTO dates (ts, d, t) VALUES ('2015-01-28 11:47:58', '2015-01-28', '11:47:58')"); Row row = session.execute("SELECT * FROM dates").one(); Date ts = row.getTimestamp("ts"); DateWithoutTime d = row.getDate("d"); long t = row.getTime("t");
The getter for
TIMESTAMP used to be called
getDate, we renamed it to
getDate is now used for the
DATE type. This is obviously a breaking change when migrating from a previous driver version, but we think the consistency was worth it.
DATE is modeled with a custom Java type called
DateWithoutTime (but the name is likely to change before 2.2 goes GA). This was not an easy decision to make, but we ruled out other possibilities:
- The new
java.timetypes from Java 8 were not an option, because we want to keep compatibility with older JDKs.
- We didn’t want to bring in an additional dependency to Joda Time. And if we had, the logical thing would have been to migrate all time types to Joda, but its
LocalTimetype does not have nanosecond resolution.
We hope to introduce custom serializers soon (JAVA-721), so that users can use their preferred Java representations.
TIME is modeled with a
long, which represents the number of nanoseconds since midnight.
New metadata for user-defined functions
Cassandra 2.2 introduces user-defined functions. Normal functions operate on individual rows:
cqlsh:test> CREATE TABLE ints(i int PRIMARY KEY); cqlsh:test> INSERT INTO ints(i) VALUES (1); cqlsh:test> INSERT INTO ints(i) VALUES (2); cqlsh:test> INSERT INTO ints(i) VALUES (3); cqlsh:test> CREATE FUNCTION inc(i int) RETURNS NULL ON NULL INPUT RETURNS int LANGUAGE java AS 'return i+1;'; cqlsh:test> select inc(i) from ints; test.inc(i) ------------- 2 3 4
Aggregates combine rows to compute a compound value:
cqlsh:test> CREATE FUNCTION plus(s int, v int) RETURNS NULL ON NULL INPUT RETURNS int LANGUAGE java AS 'return s+v;'; cqlsh:test> CREATE AGGREGATE sum(int) SFUNC plus STYPE int INITCOND 0; cqlsh:test> SELECT test.sum(i) FROM ints; test.sum(i) ------------- 6
From the client side, calling a user-defined function is not particularly different from a built-in one:
session.execute("SELECT test.sum(i) FROM ints"); // With the query builder: import static com.datastax.driver.core.querybuilder.QueryBuilder.*; session.execute(select().fcall("test.sum", raw("i")).from("ints"));
The most important addition to the driver is that functions are now exposed as metadata:
AggregateMetadata sum = cluster.getMetadata() .getKeyspace("test") .getAggregate("sum", DataType.cint()); System.out.printf("%s is an aggregate that computes a result of type %s%n", sum.getSimpleName(), sum.getReturnType()); FunctionMetadata plus = sum.getStateFunc(); System.out.printf("%s is a function that operates on %s%n", plus.getSimpleName(), plus.getArguments());
Note that, in order to retrieve a function or aggregate from a keyspace, you need to specify the argument types, to distinguish overloaded versions.
In previous driver versions, you had to provide all variables in a bound statement, or it would get rejected. This is no longer the case:
session.execute("CREATE TABLE IF NOT EXISTS unbound(k int PRIMARY KEY, v int)"); PreparedStatement pst = session.prepare("INSERT INTO unbound (k, v) VALUES (?, ?)"); // Normal execution: all values bound session.execute(pst.bind(1, 1)); int v = session.execute("SELECT v FROM unbound WHERE k = 1") .one().getInt("v"); assert v == 1; // New in 2.2: v is left unbound session.execute(pst.bind(1)); v = session.execute("SELECT v FROM unbound WHERE k = 1") .one().getInt("v"); assert v == 1;
As you can see, unset values do not overwrite previously inserted ones.
Unset values are not always legal. Cassandra will throw an error if the resulting query is invalid:
PreparedStatement pst = session.prepare("SELECT v FROM unbound WHERE k = ?"); session.execute(pst.bind()); // InvalidQueryException: Invalid unset value for column k
Cassandra now sends warnings back with the response instead of just logging them server-side. The driver exposes them through ExecutionInfo. The easiest way to observe this is to simulate a large batch:
session.execute("CREATE TABLE IF NOT EXISTS example(k int primary key, v text)"); BatchStatement batch = new BatchStatement(); batch.add(new SimpleStatement("INSERT INTO example (k, v) VALUES (1, ?)", Strings.repeat("1", 5 * 1024))); ResultSet rs = session.execute(batch); List<String> warnings = rs.getExecutionInfo().getWarnings(); assert warnings.size() == 1; // Batch of prepared statements for [test.example] is of size 5137, // exceeding specified threshold of 5120 by 17.
Getting the driver
Please note that this is a release candidate and is not meant to be used in production. The public API is subject to change until the final release. If you are planning to upgrade soon, be sure to read our upgrade guide.
We’re also running a platform and runtime survey to improve our testing infrastructure. Your feedback would be most appreciated.