|
|
|
|
@@ -21,19 +21,79 @@ such as `com.datastax.driver.core.Session` allowing you to communicate directly
|
|
|
|
|
uses consistent naming conventions on objects in various APIs to those found in the DataStax Java Driver so that they
|
|
|
|
|
are familiar and so you can map your existing knowledge onto the Spring APIs.
|
|
|
|
|
|
|
|
|
|
[[cassandra-getting-started]]
|
|
|
|
|
|
|
|
|
|
[[cassandra.modules]]
|
|
|
|
|
== Spring CQL and Spring Data Cassandra modules
|
|
|
|
|
|
|
|
|
|
Spring Data for Apache Cassandra comes with two modules: Spring CQL and Spring Data Cassandra.
|
|
|
|
|
|
|
|
|
|
The value-add provided by the Spring Data for Apache Cassandra abstraction is perhaps best shown by the sequence of actions outlined in the table below. The table shows what actions Spring will take care of and which actions are the responsibility of you, the application developer.
|
|
|
|
|
|
|
|
|
|
[[cassandra.modules.who-does-what]]
|
|
|
|
|
.Spring CQL - who does what?
|
|
|
|
|
|===
|
|
|
|
|
| Action| Spring| You
|
|
|
|
|
|
|
|
|
|
| Define connection parameters.
|
|
|
|
|
|
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
| Open the connection.
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Specify the CQL statement.
|
|
|
|
|
|
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
| Declare parameters and provide parameter values
|
|
|
|
|
|
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
| Prepare and execute the statement.
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Set up the loop to iterate through the results (if any).
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Do the work for each iteration.
|
|
|
|
|
|
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
| Process any exception.
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Close the Session.
|
|
|
|
|
| X
|
|
|
|
|
|
|
|
|
|
|
|===
|
|
|
|
|
|
|
|
|
|
Spring CQL takes care of all the low-level details that can make Cassandra and CQL such a
|
|
|
|
|
tedious API to develop with. Spring Data Cassandra adds object mapping, schema generation and repository support to the featureset.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[cassandra.choose-style]]
|
|
|
|
|
=== Choosing an approach for Cassandra database access
|
|
|
|
|
You can choose among several approaches to form the basis for your Cassandra database access. Spring's support for Apache Cassandra comes in different flavors. Once you start using one of these approaches, you can still mix and match to include a feature from a different approach.
|
|
|
|
|
|
|
|
|
|
* __CqlTemplate__ is the classic Spring CQL approach and the most popular. This "lowest level" approach and all others use a `CqlTemplate` under the covers.
|
|
|
|
|
* __CassandraTemplate__ wraps a `CqlTemplate` to provide result to object mapping and the use of SELECT, INSERT, UPDATE and DELETE methods instead of writing CQL statements. This approach provides better documentation and ease of use.
|
|
|
|
|
* __Repository Abstraction__ allows you to create repository declarations in your data access layer. The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[cassandra.getting-started]]
|
|
|
|
|
== Getting Started
|
|
|
|
|
|
|
|
|
|
Spring Data Cassandra uses the DataStax Java Driver, which supports DataStax Enterprise and Apache Cassandra. The latest commercial or open source release is recommended. The easiest way to setup
|
|
|
|
|
a working environment is to create a Spring-based project in http://spring.io/tools/sts[STS].
|
|
|
|
|
Spring Apache Cassandra support requires Cassandra 2.1 or higher, Datastax Java Driver 3.0 or higher and Java SE 6 or higher. An easy way to bootstrap setting up a working environment is to create a Spring based project in http://spring.io/tools/sts[STS].
|
|
|
|
|
|
|
|
|
|
First, you need to setup a running Cassandra server.
|
|
|
|
|
First you need to set up a running Apache Cassandra server. Refer to the http://cassandra.apache.org/doc/latest/getting_started/index.html[Apache Cassandra Quick Start guide] for an explanation on how to startup Apache Cassandra. Once installed starting Cassandra is typically a matter of executing the following command: `CASSANDRA_HOME/bin/cassandra -f`
|
|
|
|
|
|
|
|
|
|
To create a Spring project in STS go to File -> New -> Spring Template Project -> Simple Spring Utility Project ->
|
|
|
|
|
and press "Yes" when prompted. Then, enter a project and a package name such as "org.spring.data.cassandra.example".
|
|
|
|
|
|
|
|
|
|
Then add the following to the `pom.xml` dependencies section.
|
|
|
|
|
To create a Spring project in STS go to File -> New -> Spring Template Project -> Simple Spring Utility Project -> press Yes when prompted. Then enter a project and a package name such as org.spring.cassandra.example.
|
|
|
|
|
|
|
|
|
|
Then add the following to pom.xml dependencies section.
|
|
|
|
|
[source,xml,subs="verbatim,attributes"]
|
|
|
|
|
----
|
|
|
|
|
<dependencies>
|
|
|
|
|
@@ -49,44 +109,31 @@ Then add the following to the `pom.xml` dependencies section.
|
|
|
|
|
</dependencies>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Also, change the version of Spring in `pom.xml` to be
|
|
|
|
|
Also change the version of Spring in the pom.xml to be
|
|
|
|
|
|
|
|
|
|
[source,xml,subs="verbatim,attributes"]
|
|
|
|
|
----
|
|
|
|
|
<spring.framework.version>{springVersion}</spring.framework.version>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
If you are using a milestone version (e.g. M1 or RC1) of Spring Data Cassandra, you will also need to include the
|
|
|
|
|
Spring Milestone repository declaration to your `pom.xml`, like so...
|
|
|
|
|
If using a milestone release instead of a GA release, you will also need to add the location of the Spring Milestone repository for maven to your pom.xml which is at the same level of your <dependencies/> element
|
|
|
|
|
|
|
|
|
|
[source,xml]
|
|
|
|
|
----
|
|
|
|
|
<repositories>
|
|
|
|
|
<repository>
|
|
|
|
|
<id>spring-milestone</id>
|
|
|
|
|
<name>Spring Milestones Maven Repository</name>
|
|
|
|
|
<name>Spring Maven MILESTONE Repository</name>
|
|
|
|
|
<url>http://repo.spring.io/libs-milestone</url>
|
|
|
|
|
</repository>
|
|
|
|
|
</repositories>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
If you are on the bleeding edge and use build snapshots, then you will need to include the Spring Snapshots repository
|
|
|
|
|
declaration to your `pom.xml`, like so...
|
|
|
|
|
|
|
|
|
|
[source,xml]
|
|
|
|
|
----
|
|
|
|
|
<repositories>
|
|
|
|
|
<repository>
|
|
|
|
|
<id>spring-snapshot</id>
|
|
|
|
|
<name>Spring Snapshots Maven Repository</name>
|
|
|
|
|
<url>http://repo.spring.io/libs-snapshot</url>
|
|
|
|
|
</repository>
|
|
|
|
|
</repositories>
|
|
|
|
|
----
|
|
|
|
|
The repository is also http://repo.spring.io/milestone/org/springframework/data/[browseable here].
|
|
|
|
|
|
|
|
|
|
You can also browse the Spring repositories https://repo.spring.io/webapp/#/home[here].
|
|
|
|
|
|
|
|
|
|
Now we will create a simple little Java application that stores and reads a domain object to/from Cassandra.
|
|
|
|
|
Now we will create a simple Java application that stores and reads a domain object to/from Cassandra.
|
|
|
|
|
|
|
|
|
|
First, create a simple domain object class to persist.
|
|
|
|
|
|
|
|
|
|
@@ -100,35 +147,35 @@ import org.springframework.data.cassandra.mapping.Table;
|
|
|
|
|
@Table
|
|
|
|
|
public class Person {
|
|
|
|
|
|
|
|
|
|
@PrimaryKey
|
|
|
|
|
private final String id;
|
|
|
|
|
@PrimaryKey
|
|
|
|
|
private final String id;
|
|
|
|
|
|
|
|
|
|
private final String name;
|
|
|
|
|
private final int age;
|
|
|
|
|
private final String name;
|
|
|
|
|
private final int age;
|
|
|
|
|
|
|
|
|
|
public Person(String id, String name, int age) {
|
|
|
|
|
this.id = id;
|
|
|
|
|
this.name = name;
|
|
|
|
|
this.age = age;
|
|
|
|
|
}
|
|
|
|
|
public Person(String id, String name, int age) {
|
|
|
|
|
this.id = id;
|
|
|
|
|
this.name = name;
|
|
|
|
|
this.age = age;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getId() {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
public String getId() {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getName() {
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
public String getName() {
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getAge() {
|
|
|
|
|
return age;
|
|
|
|
|
}
|
|
|
|
|
public int getAge() {
|
|
|
|
|
return age;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
return String.format("{ @type = %1$s, id = %2$s, name = %3$s, age = %4$d }",
|
|
|
|
|
getClass().getName(), getId(), getName(), getAge());
|
|
|
|
|
}
|
|
|
|
|
@Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
return String.format("{ @type = %1$s, id = %2$s, name = %3$s, age = %4$d }",
|
|
|
|
|
getClass().getName(), getId(), getName(), getAge());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
@@ -139,8 +186,6 @@ Next, create the main application to run.
|
|
|
|
|
package org.spring.data.cassandra.example;
|
|
|
|
|
|
|
|
|
|
import java.io.Closeable;
|
|
|
|
|
import java.net.InetAddress;
|
|
|
|
|
import java.net.UnknownHostException;
|
|
|
|
|
import java.util.UUID;
|
|
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
@@ -155,53 +200,34 @@ import com.datastax.driver.core.querybuilder.Select;
|
|
|
|
|
|
|
|
|
|
public class CassandraApplication {
|
|
|
|
|
|
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(CassandraApplication.class);
|
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(CassandraApplication.class);
|
|
|
|
|
|
|
|
|
|
private static Cluster cluster;
|
|
|
|
|
private static Session session;
|
|
|
|
|
protected static Person newPerson(String name, int age) {
|
|
|
|
|
return newPerson(UUID.randomUUID().toString(), name, age);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static boolean close(Closeable obj) {
|
|
|
|
|
if (obj != null) {
|
|
|
|
|
try {
|
|
|
|
|
obj.close();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ignore) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
protected static Person newPerson(String id, String name, int age) {
|
|
|
|
|
return new Person(id, name, age);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
|
|
|
|
protected static Person newPerson(String name, int age) {
|
|
|
|
|
return newPerson(UUID.randomUUID().toString(), name, age);
|
|
|
|
|
}
|
|
|
|
|
Cluster cluster = Cluster.builder().addContactPoints("localhost").build();
|
|
|
|
|
Session session = cluster.connect("mykeyspace");
|
|
|
|
|
|
|
|
|
|
protected static Person newPerson(String id, String name, int age) {
|
|
|
|
|
return new Person(id, name, age);
|
|
|
|
|
}
|
|
|
|
|
CassandraOperations template = new CassandraTemplate(session);
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) throws UnknownHostException {
|
|
|
|
|
try {
|
|
|
|
|
cluster = Cluster.builder().addContactPoints(InetAddress.getLocalHost()).build();
|
|
|
|
|
session = cluster.connect("mykeyspace");
|
|
|
|
|
Person jonDoe = template.insert(newPerson("Jon Doe", 40));
|
|
|
|
|
|
|
|
|
|
CassandraOperations template = new CassandraTemplate(session);
|
|
|
|
|
Select selectStatement = QueryBuilder.select().from("person");
|
|
|
|
|
selectStatement.where(QueryBuilder.eq("id", jonDoe.getId()));
|
|
|
|
|
|
|
|
|
|
Person jonDoe = template.insert(newPerson("Jon Doe", 40));
|
|
|
|
|
LOGGER.info(template.queryForObject(selectStatement, Person.class).getId());
|
|
|
|
|
|
|
|
|
|
Select selectStatement = QueryBuilder.select().from("person");
|
|
|
|
|
selectStatement.where(QueryBuilder.eq("id", jonDoe.getId()));
|
|
|
|
|
|
|
|
|
|
LOGGER.info(template.queryForObject(selectStatement, Person.class).getId());
|
|
|
|
|
|
|
|
|
|
template.truncate("person");
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
close(session);
|
|
|
|
|
close(cluster);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
template.truncate("person");
|
|
|
|
|
session.close();
|
|
|
|
|
cluster.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
@@ -212,15 +238,164 @@ Even in this simple example, there are a few things to observe.
|
|
|
|
|
override these mapping names to match your Cassandra database table and column names.
|
|
|
|
|
* You can either use a CQL String or the DataStax `QueryBuilder` API to construct you queries.
|
|
|
|
|
|
|
|
|
|
[[cassandra.examples-repo]]
|
|
|
|
|
== Examples Repository
|
|
|
|
|
|
|
|
|
|
After the initial release of Spring Data for Apache Cassandra 1.0.0, we will start working on a showcase repository with full examples.
|
|
|
|
|
There is a https://github.com/spring-projects/spring-data-examples[Github repository with several examples] that you can download and play around with to get a feel for how the library works.
|
|
|
|
|
|
|
|
|
|
[[cassandra-connectors]]
|
|
|
|
|
[[cassandra.connectors]]
|
|
|
|
|
== Connecting to Cassandra with Spring
|
|
|
|
|
|
|
|
|
|
[[cassandra-connectors.ext_properties]]
|
|
|
|
|
=== Externalize Connection Properties
|
|
|
|
|
One of the first tasks when using Apache Cassandra and Spring is to create a `com.datastax.driver.core.Session` object using the IoC container. There are two main ways to do this, either using Java based bean metadata or XML based bean metadata. These are discussed in the following sections.
|
|
|
|
|
|
|
|
|
|
NOTE: For those not familiar with how to configure the Spring container using Java based bean metadata instead of XML based metadata see the high level introduction in the reference docs http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.0.html#new-java-configuration[here ] as well as the detailed documentation http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html#beans-java-instantiating-container[ here].
|
|
|
|
|
|
|
|
|
|
[[cassandra.cassandra-java-config]]
|
|
|
|
|
=== Registering a Session instance using Java based metadata
|
|
|
|
|
|
|
|
|
|
An example of using Java based bean metadata to register an instance of a `com.datastax.driver.core.Session` is shown below
|
|
|
|
|
|
|
|
|
|
.Registering a com.datastax.driver.core.Session object using Java based bean metadata
|
|
|
|
|
====
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
public class AppConfig {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Use the standard Cassandra driver API to create a com.datastax.driver.core.Session instance.
|
|
|
|
|
*/
|
|
|
|
|
public @Bean Session session() {
|
|
|
|
|
Cluster cluster = Cluster.builder().addContactPoints("localhost").build();
|
|
|
|
|
return cluster.connect("mykeyspace");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
This approach allows you to use the standard `com.datastax.driver.core.Session` API that you may already be used to using.
|
|
|
|
|
|
|
|
|
|
An alternative is to register an instance of `com.datastax.driver.core.Session` instance with the container using Spring's `CassandraCqlSessionFactoryBean` and `CassandraCqlClusterFactoryBean`. As compared to instantiating a `com.datastax.driver.core.Session` instance directly, the `FactoryBean` approach has the added advantage of also providing the container with an ExceptionTranslator implementation that translates Cassandra exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html[Spring's DAO support features].
|
|
|
|
|
|
|
|
|
|
An example of a Java based bean metadata that supports exception translation on `@Repository` annotated classes is shown below:
|
|
|
|
|
|
|
|
|
|
.Registering a com.datastax.driver.core.Session object using Spring's CassandraCqlSessionFactoryBean and enabling Spring's exception translation support
|
|
|
|
|
====
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
public class AppConfig {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Factory bean that creates the com.datastax.driver.core.Session instance
|
|
|
|
|
*/
|
|
|
|
|
public @Bean CassandraCqlClusterFactoryBean cluster() {
|
|
|
|
|
|
|
|
|
|
CassandraCqlClusterFactoryBean cluster = new CassandraCqlClusterFactoryBean();
|
|
|
|
|
cluster.setContactPoints("localhost");
|
|
|
|
|
|
|
|
|
|
return cluster;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Factory bean that creates the com.datastax.driver.core.Session instance
|
|
|
|
|
*/
|
|
|
|
|
public @Bean CassandraCqlSessionFactoryBean session() {
|
|
|
|
|
|
|
|
|
|
CassandraCqlSessionFactoryBean session = new CassandraCqlSessionFactoryBean();
|
|
|
|
|
session.setCluster(cluster().getObject());
|
|
|
|
|
session.setKeyspaceName("mykeyspace");
|
|
|
|
|
|
|
|
|
|
return session;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Using `CassandraTemplate` with object mapping and repository support requires a `CassandraTemplate`, `CassandraMappingContext`, `CassandraConverter` and enabling repository support.
|
|
|
|
|
|
|
|
|
|
.Registering components to configure object mapping and repository support
|
|
|
|
|
====
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
@EnableCassandraRepositories(basePackages = { "org.spring.cassandra.example.repo" })
|
|
|
|
|
public class CassandraConfig {
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraClusterFactoryBean cluster() {
|
|
|
|
|
|
|
|
|
|
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
|
|
|
|
|
cluster.setContactPoints("localhost");
|
|
|
|
|
|
|
|
|
|
return cluster;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraMappingContext mappingContext() {
|
|
|
|
|
|
|
|
|
|
BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
|
|
|
|
|
mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(), "mykeyspace"));
|
|
|
|
|
|
|
|
|
|
return mappingContext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraConverter converter() {
|
|
|
|
|
return new MappingCassandraConverter(mappingContext());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraSessionFactoryBean session() throws Exception {
|
|
|
|
|
|
|
|
|
|
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
|
|
|
|
|
session.setCluster(cluster().getObject());
|
|
|
|
|
session.setKeyspaceName("mykeyspace");
|
|
|
|
|
session.setConverter(converter());
|
|
|
|
|
session.setSchemaAction(SchemaAction.NONE);
|
|
|
|
|
|
|
|
|
|
return session;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraOperations cassandraTemplate() throws Exception {
|
|
|
|
|
return new CassandraTemplate(session().getObject());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
Creating configuration classes registering Spring Data Cassandra components can get an exhausing challenge so Spring Data Cassandra comes with a prebuilt configuration support class. Classes extending from `AbstractCassandraConfiguration` will register beans for Spring Data Cassandra use. `AbstractCassandraConfiguration` lets you provide various configuration options such as initial entities, default query options, socket options, pooling options and much more. `AbstractCassandraConfiguration` will support you also with schema generation based on initial entities, if any provided. Extending from `AbstractCassandraConfiguration` requires you to at least provide the keyspace name by implementing the `getKeyspaceName` method.
|
|
|
|
|
|
|
|
|
|
.Registering Spring Data Cassandra beans using AbstractCassandraConfiguration
|
|
|
|
|
====
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
@Configuration
|
|
|
|
|
public class AppConfig extends AbstractCassandraConfiguration {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Provide a contact point to the configuration.
|
|
|
|
|
*/
|
|
|
|
|
public String getContactPoints() {
|
|
|
|
|
return "localhost";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Provide a keyspace name to the configuration.
|
|
|
|
|
*/
|
|
|
|
|
public getKeyspaceName() {
|
|
|
|
|
return "mykeyspace";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[[cassandra-connectors.xmlconfig]]
|
|
|
|
|
=== XML Configuration
|
|
|
|
|
|
|
|
|
|
[[cassandra-connectors.xmlconfig.ext_properties]]
|
|
|
|
|
==== Externalize Connection Properties
|
|
|
|
|
|
|
|
|
|
Create a properties file containing the information needed to connect to Cassandra. `contactpoints` and `keyspace`
|
|
|
|
|
are required fields; `port` has been added for clarity.
|
|
|
|
|
@@ -236,10 +411,67 @@ cassandra.keyspace=showcase
|
|
|
|
|
|
|
|
|
|
We will use Spring to load these properties into the Spring context in the next two examples.
|
|
|
|
|
|
|
|
|
|
[[cassandra-connectors.xmlconfig]]
|
|
|
|
|
=== XML Configuration
|
|
|
|
|
==== Registering a Session instance using XML based metadata
|
|
|
|
|
|
|
|
|
|
The XML Configuration elements for a basic Cassandra configuration are shown below. These elements all use default bean names to keep the configuration code clean and readable.
|
|
|
|
|
While you can use Spring's traditional `<beans/>` XML namespace to register an instance of `com.datastax.driver.core.Session` with the container, the XML can be quite verbose as it is general purpose. XML namespaces are a better alternative to configuring commonly used objects such as the Session instance. The `cql` and `cassandra` namespaces allow you to create a Session instance.
|
|
|
|
|
|
|
|
|
|
To use the Mongo namespace elements you will need to reference the Mongo schema:
|
|
|
|
|
|
|
|
|
|
.XML schema to configure Cassandra using the `cql` namespace
|
|
|
|
|
====
|
|
|
|
|
[source,xml]
|
|
|
|
|
----
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
|
|
|
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
|
|
xmlns:cql="http://www.springframework.org/schema/data/cql"
|
|
|
|
|
xmlns:context="http://www.springframework.org/schema/context"
|
|
|
|
|
xsi:schemaLocation="http://www.springframework.org/schema/cql http://www.springframework.org/schema/cql/spring-cql-1.0.xsd
|
|
|
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
|
|
|
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
|
|
|
|
|
|
|
|
|
<!-- Default bean name is 'cassandraCluster' -->
|
|
|
|
|
<cql:cluster contact-points="localhost" port="9042">
|
|
|
|
|
<cql:keyspace action="CREATE_DROP" name="mykeyspace" />
|
|
|
|
|
</cql:cluster>
|
|
|
|
|
|
|
|
|
|
<!-- Default bean name is 'cassandraSession' -->
|
|
|
|
|
<cql:session keyspace-name="mykeyspace" />
|
|
|
|
|
|
|
|
|
|
</beans>
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
.XML schema to configure Cassandra using the `cassandra` namespace
|
|
|
|
|
====
|
|
|
|
|
[source,xml]
|
|
|
|
|
----
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
|
|
|
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
|
|
xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
|
|
|
|
|
xmlns:context="http://www.springframework.org/schema/context"
|
|
|
|
|
http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
|
|
|
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
|
|
|
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
|
|
|
|
|
|
|
|
|
<!-- Default bean name is 'cassandraCluster' -->
|
|
|
|
|
<cassandra:cluster contact-points="localhost" port="9042">
|
|
|
|
|
<cassandra:keyspace action="CREATE_DROP" name="mykeyspace" />
|
|
|
|
|
</cassandra:cluster>
|
|
|
|
|
|
|
|
|
|
<!-- Default bean name is 'cassandraSession' -->
|
|
|
|
|
<cass:session keyspace-name="${cassandra.keyspace}" schema-action="NONE" />
|
|
|
|
|
|
|
|
|
|
</beans>
|
|
|
|
|
----
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
NOTE: You may have noticed the slight difference between namespaces: `cql` and `cassandra`. Using the `cql` namespace is limited to low level CQL support while `cassandra` extends the `cql` namespace by object mapping and schema generation support.
|
|
|
|
|
|
|
|
|
|
The XML Configuration elements for a more advanced Cassandra configuration are shown below. These elements all use default bean names to keep the configuration code clean and readable.
|
|
|
|
|
|
|
|
|
|
While this example show how easy it is to configure Spring to connect to Cassandra, there are many other options. Basically, any option available with the DataStax Java Driver is also available in the Spring Data for Apache Cassandra configuration. This is including, but not limited to Authentication, Load Balancing Policies, Retry Policies and Pooling Options. All of the Spring Data for Apache Cassandra method names and XML elements are named exactly (or as close as possible) like the configuration options on the driver so mapping any existing driver configuration should be straight forward.
|
|
|
|
|
|
|
|
|
|
@@ -247,12 +479,12 @@ While this example show how easy it is to configure Spring to connect to Cassand
|
|
|
|
|
----
|
|
|
|
|
<?xml version='1.0'?>
|
|
|
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
|
|
|
|
|
xmlns:context="http://www.springframework.org/schema/context"
|
|
|
|
|
xsi:schemaLocation="http://www.springframework.org/schema/cql http://www.springframework.org/schema/cql/spring-cql-1.0.xsd
|
|
|
|
|
http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
|
|
|
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
|
|
|
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
|
|
|
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
|
|
xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
|
|
|
|
|
xmlns:context="http://www.springframework.org/schema/context"
|
|
|
|
|
http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
|
|
|
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
|
|
|
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
|
|
|
|
|
|
|
|
|
<!-- Loads the properties into the Spring Context and uses them to fill
|
|
|
|
|
in placeholders in the bean definitions -->
|
|
|
|
|
@@ -267,7 +499,9 @@ While this example show how easy it is to configure Spring to connect to Cassand
|
|
|
|
|
<cassandra:session keyspace-name="${cassandra.keyspace}" />
|
|
|
|
|
|
|
|
|
|
<!-- REQUIRED: The Default Cassandra Mapping Context used by CassandraConverter -->
|
|
|
|
|
<cassandra:mapping />
|
|
|
|
|
<cassandra:mapping>
|
|
|
|
|
<cassandra:user-type-resolver keyspace-name="${cassandra.keyspace}" />
|
|
|
|
|
</cassandra:mapping>
|
|
|
|
|
|
|
|
|
|
<!-- REQUIRED: The Default Cassandra Converter used by CassandraTemplate -->
|
|
|
|
|
<cassandra:converter />
|
|
|
|
|
@@ -283,90 +517,25 @@ While this example show how easy it is to configure Spring to connect to Cassand
|
|
|
|
|
</beans>
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[cassandra-connectors.javaconfig]]
|
|
|
|
|
=== Java Configuration
|
|
|
|
|
|
|
|
|
|
The following class show a basic and minimal Cassandra configuration using the AnnotationConfigApplicationContext (aka JavaConfig).
|
|
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
|
----
|
|
|
|
|
package org.spring.cassandra.example.config;
|
|
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.context.annotation.Bean;
|
|
|
|
|
import org.springframework.context.annotation.Configuration;
|
|
|
|
|
import org.springframework.context.annotation.PropertySource;
|
|
|
|
|
import org.springframework.core.env.Environment;
|
|
|
|
|
import org.springframework.data.cassandra.config.CassandraClusterFactoryBean;
|
|
|
|
|
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
|
|
|
|
|
import org.springframework.data.cassandra.config.SchemaAction;
|
|
|
|
|
import org.springframework.data.cassandra.convert.CassandraConverter;
|
|
|
|
|
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
|
|
|
|
|
import org.springframework.data.cassandra.core.CassandraOperations;
|
|
|
|
|
import org.springframework.data.cassandra.core.CassandraTemplate;
|
|
|
|
|
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
|
|
|
|
|
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
|
|
|
|
|
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
|
|
|
|
|
|
|
|
|
|
@Configuration
|
|
|
|
|
@PropertySource(value = { "classpath:cassandra.properties" })
|
|
|
|
|
@EnableCassandraRepositories(basePackages = { "org.spring.cassandra.example.repo" })
|
|
|
|
|
public class CassandraConfig {
|
|
|
|
|
|
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(CassandraConfig.class);
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private Environment env;
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraClusterFactoryBean cluster() {
|
|
|
|
|
|
|
|
|
|
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
|
|
|
|
|
cluster.setContactPoints(env.getProperty("cassandra.contactpoints"));
|
|
|
|
|
cluster.setPort(Integer.parseInt(env.getProperty("cassandra.port")));
|
|
|
|
|
|
|
|
|
|
return cluster;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraMappingContext mappingContext() {
|
|
|
|
|
return new BasicCassandraMappingContext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraConverter converter() {
|
|
|
|
|
return new MappingCassandraConverter(mappingContext());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraSessionFactoryBean session() throws Exception {
|
|
|
|
|
|
|
|
|
|
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
|
|
|
|
|
session.setCluster(cluster().getObject());
|
|
|
|
|
session.setKeyspaceName(env.getProperty("cassandra.keyspace"));
|
|
|
|
|
session.setConverter(converter());
|
|
|
|
|
session.setSchemaAction(SchemaAction.NONE);
|
|
|
|
|
|
|
|
|
|
return session;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
|
public CassandraOperations cassandraTemplate() throws Exception {
|
|
|
|
|
return new CassandraTemplate(session().getObject());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[[cassandra.auditing]]
|
|
|
|
|
== General auditing configuration
|
|
|
|
|
|
|
|
|
|
Auditing support is not available in the current version.
|
|
|
|
|
|
|
|
|
|
[[cassandra-template]]
|
|
|
|
|
== Introduction to CassandraTemplate
|
|
|
|
|
|
|
|
|
|
The class `CassandraTemplate`, located in the package `org.springframework.data.cassandra`, is the central class of the Spring's Cassandra support providing a rich feature set to interact with the database. The template offers convenience operations to create, update, delete and query Cassandra and provides a mapping between your domain objects and Cassandra rows.
|
|
|
|
|
|
|
|
|
|
NOTE: Once configured, `CassandraTemplate` is thread-safe and can be reused across multiple instances.
|
|
|
|
|
|
|
|
|
|
The mapping between Cassandra rows and domain classes is done by delegating to an implementation of the interface `CassandraConverter`. Spring provides a default implementation, `MappingCassandraConverter`, but you can also write your own converter. Please refer to the section on <<mapping-chapter,Cassandra conversion>> for more detailed information.
|
|
|
|
|
|
|
|
|
|
The `CassandraTemplate` class implements the interface `CassandraOperations`. In as much as possible, the methods on `CassandraOperations` are named after methods available with Cassandra to make the API familiar to existing Cassandra developers who are used to Cassandra. For example, you will find methods such as "select", "insert", "delete", and "update". The design goal was to make it as easy as possible to transition between the use of the base Cassandra driver and `CassandraOperations`. A major difference in between the two APIs is that `CassandraOperations` can be passed domain objects instead of CQL and query objects.
|
|
|
|
|
|
|
|
|
|
NOTE: The preferred way to reference the operations on `CassandraTemplate` instance is via its interface `CassandraOperations`.
|
|
|
|
|
|
|
|
|
|
The default converter implementation used by `CassandraTemplate` is `MappingCassandraConverter`. While the `MappingCassandraConverter` can make use of additional metadata to specify the mapping of objects to rows it is also capable of converting objects that contain no additional metadata by using some conventions for the mapping of fields and table names. These conventions as well as the use of mapping annotations is explained in the <<mapping.chapter,Mapping chapter>>.
|
|
|
|
|
|
|
|
|
|
Another central feature of `CassandraTemplate` is exception translation of exceptions thrown in the Cassandra Java driver into Spring's portable Data Access Exception hierarchy. Refer to the section on <<cassandra.exception,exception translation>> for more information.
|
|
|
|
|
|
|
|
|
|
Now let's look at a examples of how to work with the `CassandraTemplate` in the context of the Spring container.
|
|
|
|
|
|
|
|
|
|
[[cassandra-template.instantiating]]
|
|
|
|
|
=== Instantiating CassandraTemplate
|
|
|
|
|
|
|
|
|
|
@@ -770,7 +939,7 @@ String cqlAll = "select * from person";
|
|
|
|
|
|
|
|
|
|
List<Person> results = cassandraOperations.select(cqlAll, Person.class);
|
|
|
|
|
for (Person p : results) {
|
|
|
|
|
LOG.info(String.format("Found People with Name [%s] for id [%s]", p.getName(), p.getId()));
|
|
|
|
|
LOG.info(String.format("Found People with Name [%s] for id [%s]", p.getName(), p.getId()));
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
@@ -950,7 +1119,7 @@ cassandraOperations.execute(dropper);
|
|
|
|
|
[[cassandra.exception]]
|
|
|
|
|
== Exception Translation
|
|
|
|
|
|
|
|
|
|
The Spring framework provides exception translation for a wide variety of database and mapping technologies. This has traditionally been for JDBC and JPA. The Spring support for Cassandra extends this feature to the Cassandra Database by providing an implementation of the `org.springframework.dao.support.PersistenceExceptionTranslator` interface.
|
|
|
|
|
The Spring framework provides exception translation for a wide variety of database and mapping technologies. This has traditionally been for JDBC and JPA. The Spring support for Cassandra extends this feature to Cassandra by providing an implementation of the `org.springframework.dao.support.PersistenceExceptionTranslator` interface.
|
|
|
|
|
|
|
|
|
|
The motivation behind mapping to Spring's http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions[consistent data access exception hierarchy] is that you are then able to write portable and descriptive exception handling code without resorting to coding against Cassandra Exceptions. All of Spring's data access exceptions are inherited from the root `DataAccessException` class so you can be sure that you will be able to catch all database related exception within a single try-catch block.
|
|
|
|
|
|
|
|
|
|
|