Cassandra for Java - 1.0

Extension ID

com.castsoftware.java.cassandra

What’s New?

Please see Cassandra for Java - 1.0 - Release Notes for more information.

In what situation should you install this extension?

This extension is specifically targeted at the Cassandra for Java framework and should be used in conjunction with the JEE Analyzer extension and the Cassandra extension. CRUD operations and Queries (@Query annotations) are supported, along with datastax queries. When client code uses any of these coding mechanisms, this extension, along with Cassandra extension, will create the links from the calling method to the Cassandra Java CQL Query and between the Cassandra Java CQL Query and the Cassandra Java Table/Cassandra CQL Table.

Note that there are two types of table because they may originate in .cql files, Cassandra CQL Table, or in java code, Cassandra Java Table. The Cassandra extension is responsible for creating the tables and the links. This helps form the complete transaction.

Supported releases

Apache Cassandra Supported
4.x (tick)
3.x (tick)
2.x (tick)
1.x (tick)

AIP Core compatibility

This extension is compatible with:

AIP Core release Supported
8.3.x (tick)

Dependencies with other extensions

Some CAST extensions require the presence of other CAST extensions in order to function correctly. The Cassandra for Java extension requires that the following other CAST extensions are also installed:

  • CAST AIP Internal extension (internal technical extension)
  • Cassandra extension

Note that when using the CAST Extension Downloader to download the extension and the Manage Extensions interface in CAST Server Manager to install the extension, any dependent extensions are automatically downloaded and installed for you. You do not need to do anything.

Download and installation instructions

You will need to manually install the extension using the Application - Extension interface in CAST Console.

What results can you expect?

Once the analysis/snapshot generation has completed, you can view the results in the normal manner. Some codeexamples are also shown below.

Objects

Icon Object Type

Cassandra Java Operation

Cassandra Java CQL Query

Cassandra Java Schema

Cassandra Java Table

Cassandra Java Table Column

Cassandra Java Index

Cassandra Java Primary Key

Database access with Spring Framework

Spring Data repository abstraction is focused on abstracting out the code required to implement the data access layers across different persistence mechanisms. CassandraRepository is used for data access layer. Using @Table annotation, the bean is directly mapped to a Cassandra data table. Also each property is defined as a type of primary key or a simple column.

Native queries (@Query Annotation)

When using @Query annotation, we create a Cassandra Java CQL Query object. The query Java code example:

DataTableRepository.java

import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.Query;

import java.util.Optional;

public interface DataTableRepository
        extends CassandraRepository<DataTable, DataTableKey>, DataTableRepositoryCustom {

    @Query("SELECT * FROM data_table WHERE table_id = ?0 and year = ?1 order by ts DESC limit 1")
    Optional<SensorIndex> findLatestTable(final String tableId, final Integer year);

}

The table Java class:

DataTable.java

import org.springframework.data.cassandra.core.mapping.Column;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
import java.math.BigDecimal;
@Value
@Builder(builderClassName = "Builder")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)

@Table("data_table")

public class DataTable {

    @PrimaryKey
    private final DataTableKey key;

    @Column("value")
    private final BigDecimal value; 

}

The CQL file containing table creation code:

data_table.cql

CREATE TABLE data_table (
    key text,
    value text
    PRIMARY KEY ((key), value));

The expected result:

CRUD method calls

The CRUD method calls supported with the Java Cassandra extension are enumerated in the table below:

Class

Methods
org.springframework.data.repository.CrudRepository count, delete, deleteAll, deleteById, existsById, findAll, findAllById, findById, findOne, save
org.springframework.data.cassandra.repository.CassandraRepository count, delete, deleteAll, deleteAllById, existsById, findAll, findAllById, findById, insert, save, saveAll
org.springframework.data.cassandra.repository.ReactiveCassandraRepository count, delete, deleteAll, deleteAllById, deleteById, existsById, findAll, findAllById, findById, insert, save, saveAll
org.springframework.data.cassandra.repository.TypedIdCassandraRepository count, delete, deleteAll, exists, findAll, findOne, save

For CRUD calls, as we don’t have the text query, CAST creates a new object called Cassandra Java Operation. The name of the object is Select/Insert/Update/Delete, depending on the type of call. It inherits from the following Cassandra objects and the property tableName has as value the name of the table to which it will be linked:

  • CAST_Cassandra_Select_Table
  • CAST_Cassandra_Insert_Table
  • CAST_Cassandra_Update_Table
  • CAST_Cassandra_Delete_Table

Example of Cassandra Java Operation Update:

SensorIndexServiceImpl.java

public class SensorIndexServiceImpl implements SensorIndexService {

    SensorIndexRepository sensorIndexRepository;

    @Override
    public SensorIndex save(final SensorIndex sensorIndex) {
        return sensorIndexRepository.save(sensorIndex);
    }
}

The repository Java class:

SensorIndexRepository.java

import org.springframework.data.cassandra.repository.CassandraRepository;

public interface SensorIndexRepository
        extends CassandraRepository<SensorIndex, SensorIndexKey>, SensorIndexRepositoryCustom {
    ...

}

The table Java class:

SensorIndex.java

import org.springframework.data.cassandra.core.mapping.Column;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
@Value
@Builder(builderClassName = "Builder")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)

@Table("sensor_index")

public class SensorIndex {

    @PrimaryKey
    private final SensorIndexKey key;

    @Column("value")
    private final BigDecimal value;
}

The CQL file containing table creation code:

sensor_index.cql

CREATE TABLE IF NOT EXISTS sensor_index(sensor_id text, year int, ts timestamp ,value decimal, PRIMARY KEY ((sensor_id, year), ts));

The expected result:

Query method calls

Similar to CRUD calls CAST supports the query method calls. Example of Cassandra Java Operation Select:

EmployeeRepository.java

import org.springframework.data.cassandra.repository.CassandraRepository;
import reactor.core.publisher.Flux;

public interface EmployeeRepository extends CassandraRepository<Employee, Integer> {
    @AllowFiltering
    Flux<Employee> findByAgeGreaterThan(int age);
}

The CQL file containing table creation code:

employee.cql

CREATE TABLE employee (
    id text,
    name text
    PRIMARY KEY ((id), name));

The expected result:

CassandraOperations and CassandraTemplate

CQL queries can be built using org.springframework.data.cassandra.core.CassandraOperations and org.springframework.data.cassandra.core.CassandraTemplate class APIs. The supported APIs are: execute, select, insert, update, delete.

Example of select query:

EmployeeRepository.java

import org.springframework.data.cassandra.core.CassandraOperations;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;

public class IntradayWeatherRepositoryImpl implements IntradayWeatherRepositoryCustom {    
    @Override
    public List<IntradayWeather> findByTs(int weatherStationId, Set<Integer> years, Date begin, Date end) {
        final Select select = QueryBuilder.select().all().from(template.getTableName(IntradayWeather.class).toCql());
        select.where(QueryBuilder.eq(WEATHER_STATION_ID, weatherStationId))
                .and(QueryBuilder.in(YEAR_COLUMN_NAME, years.toArray()))
                .and(QueryBuilder.gte(TEMPERATURE_TS_COLUMN_NAME, begin))
                .and(QueryBuilder.lt(TEMPERATURE_TS_COLUMN_NAME, end));

        return template.select(select, IntradayWeather.class);
    }
}

The CQL file containing table creation code:

intraday_weather.cql

CREATE TABLE IF NOT EXISTS intraday_weather(weather_station_id int, year int, temperature_ts timestamp,production_ts timestamp,temperature decimal, pictogram text, PRIMARY KEY ((weather_station_id, year), temperature_ts));

The expected result:

Example of insert query:

CassandraApplication.java

import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.core.query.Criteria;
import org.springframework.data.cassandra.core.query.Query;

import com.datastax.oss.driver.api.core.CqlSession;

public class CassandraApplication {

  private static final Log LOG = LogFactory.getLog(CassandraApplication.class);

  private static Person newPerson(String name, int age) {
    return new Person(UUID.randomUUID().toString(), name, age);
  }

  public static void main(String[] args) {

    CqlSession cqlSession = CqlSession.builder().withKeyspace("mykeyspace").build();
    CassandraOperations template = new CassandraTemplate(cqlSession);

    Person jonDoe = template.insert(newPerson("Jon Doe", 40));

    cqlSession.close();
  }

}

Person.java

import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;

@Table
public class Person {

  @PrimaryKey private final String id;

  private final String name;

  public Person(String id, String name) {
    this.id = id;
    this.name = name;
  }

  public String getId() {
    return id;
  }

  private String getName() {
    return name;
  }
}

The CQL file containing table creation code:

Person.cql

CREATE TABLE IF NOT EXISTS Person(id int, name text);

The expected result:

CqlTemplate

CQL queries can be built using org.springframework.cassandra.core.CqlTemplate class APIs.

The supported APIs are: execute, query, queryForObject, queryForList.

Example of queryForObject and execute APIs.

Example.java

import org.springframework.cassandra.core.CqlTemplate;

public class Example {
    
    private static CqlTemplate cqlTemplate;
    
    public static void main(String[] args) {

        int countOfActorsNamedJoe = cqlTemplate.queryForObject(
        "SELECT COUNT(*) FROM t_actor WHERE first_name = ?", Integer.class);

        cqlTemplate.execute("CREATE TABLE t_actor (id uuid primary key, first_name text)");
    }
}

The expected results:

Database access with DataStax

Supported DataStax Classes

Example of main DataStax classes used for table access
  • com.datastax.driver.core.Session
  • com.datastax.oss.driver.core.Session
  • com.datastax.oss.driver.api.core.CqlSession
  • com.datastax.oss.driver.api.querybuilder.BuildableQuery
  • com.datastax.oss.driver.api.querybuilder.SchemaBuilder
  • com.datastax.oss.driver.api.core.session.SessionBuilder
  • com.datastax.oss.driver.api.core.cql.session.SessionBuilder
  • com.datastax.oss.driver.api.querybuilder.QueryBuilder
  • com.datastax.oss.driver.api.querybuilder.schema.CreateTable

The APIs com.datastax.oss.driver.api.querybuilder.QueryBuilder and com.datastax.oss.driver.api.querybuilder.SchemaBuilder allow the generation of CQL queries programmatically for Cassandra NoSQL. There are various ways to build the queries. You can either build (com.datastax.oss.driver.api.core.cql.session.SessionBuilder.build) or execute (com.datastax.driver.core.Session.execute, com.datastax.oss.driver.core.Session.execute, com.datastax.oss.driver.api.core.CqlSession.execute) these queries.

Tables are created either via *.cql files, and in this case, as in the example above, we have Cassandra CQL Table objects, either programmatically in java via queries. In the second case, we have Cassandra Java Table objects.

Example of Create Table Query in Java with StringBuilder API

BookRepository.java

public class BookRepository {

    private static final String TABLE_NAME = "books";
    private Session session;

    public BookRepository(Session session) {
        this.session = session;
    }

    public void createTable() {
        StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS ").append(TABLE_NAME).append("(").append("id uuid PRIMARY 
                                    KEY, ").append("title text,").append("author text,").append("subject text);");
        final String query = sb.toString();
        session.execute(query);
    }
    ...
}

The expected result:

Example of Select Query in Java with StringBuilder API

BookRepository.java

public class BookRepository {

    private static final String TABLE_NAME = "books";
    private Session session;

    public BookRepository(Session session) {
        this.session = session;
    }

    public List<Book> selectAll() {
        StringBuilder sb = new StringBuilder("SELECT * FROM ").append(TABLE_NAME);

        final String query = sb.toString();
        ResultSet rs = session.execute(query);

        List<Book> books = new ArrayList<Book>();

        for (Row r : rs) {
            Book book = new Book(r.getUUID("id"), r.getString("title"), r.getString("author"), r.getString("subject"));
            books.add(book);
        }
        return books;
    }
    ...
}

The expected result:

Example of Create Table in Java with SchemaBuilder API

The SchemaBuilder is an additional API provided by java driver query builder that enables generating CQL DDL queries programmatically. It is used to:

  • generate schema queries based on application configuration
  • generate representative schema DDL CREATE queries, given a Java class that represents a table, view, or user defined type.

ProductRepository.java

public class ProductRepository {

    private static final String PRODUCT_TABLE_NAME = "product";
    private final CqlSession session;

    public ProductRepository(CqlSession session) {
        this.session = session;
    }
    
    public void createProductTable(String keyspace) {
        CreateTable createTable = SchemaBuilder.createTable(PRODUCT_TABLE_NAME).ifNotExists()
                .withPartitionKey("product_id", DataTypes.UUID)
                .withColumn("product_name", DataTypes.TEXT)
                .withColumn("description", DataTypes.TEXT)
                .withColumn("price", DataTypes.FLOAT);

        executeStatement(createTable.build(), keyspace);
    }

    private ResultSet executeStatement(SimpleStatement statement, String keyspace) {
        if (keyspace != null) {
            statement.setKeyspace(CqlIdentifier.fromCql(keyspace));
        }

        return session.execute(statement);
    }
}

The expected result:

Example of Create keyspace in Java with SchemaBuilder API

Following table creation example, keyspace are created (Cassandra Java Schema objects).

KeyspaceRepository.java

public class KeyspaceRepository {
    private final CqlSession session;

    public KeyspaceRepository(CqlSession session) {
        this.session = session;
    }

    public void createKeyspace(String keyspaceName, int numberOfReplicas) {
        CreateKeyspace createKeyspace = SchemaBuilder.createKeyspace(keyspaceName)
          .ifNotExists()
          .withSimpleStrategy(numberOfReplicas);

        session.execute(createKeyspace.build());
    }
}

CassandraClient.java

public class CassandraClient {
    private static final Logger LOG = LoggerFactory.getLogger(CassandraClient.class);

    public static void main(String args[]) {
        CassandraConnector connector = new CassandraConnector();
        connector.connect("127.0.0.1", null);
        Session session = connector.getSession();

        KeyspaceRepository sr = new KeyspaceRepository(session);
        sr.createKeyspace("library", 1);
        sr.useKeyspace("library");
    }
}

The expected result:

Example of Alter Table in Java with SchemaBuilder API

BookRepository.java

    public void alterTable(String keyspace) {
        BuildableQuery alterTable = SchemaBuilder.alterTable("books")
          .alterColumn("publisher", DataTypes.Text);

        executeStatement(alterTable.build(), keyspace);
    }

The expected result:

Example of Create Index in Java with SchemaBuilder API

KeyspaceRepository.java

    public void createIndex() {
        CreateIndex index = SchemaBuilder.createIndex("video_games")
                .ifNotExists()
                .onTable("videos")
                .andColumn("editor");
        session.execute(index.build());
    }

The expected result: