Introduction to Spring Data Cassandra – Spring数据卡桑德拉简介

最后修改: 2015年 12月 6日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This article is a practical introduction to working with Cassandra with Spring Data.

本文是对使用Spring Data的Cassandra工作的实用介绍。

We’ll start the from the basics and go through the configurations and coding, finally building up a complete Spring Data Cassandra module.

我们将从基础知识开始,通过配置和编码,最终建立起一个完整的Spring Data Cassandra模块。

2. Maven Dependencies

2.Maven的依赖性

Let’s start by defining the dependencies in the pom.xml, with Maven:

我们先用Maven在pom.xml中定义依赖项。

<dependency>
    <groupId>com.datastax.cassandra</groupId>
    <artifactId>cassandra-driver-core</artifactId>
    <version>2.1.9</version>
</dependency>

3. Configuration for Cassandra

3.Cassandra的配置

We’ll use the Java style of configuration throughout this quite to configure the Cassandra integration.

我们将在本篇中使用Java风格的配置来配置Cassandra集成。

3.1. Main Config (Spring)

3.1.主配置(Spring)

We’ll use the Java style of configuration for this. Let’s start with the main configuration class – of course driven via the class level @Configuration annotation:

我们将为此使用Java风格的配置。让我们从主配置类开始–当然是通过类级的@Configuration 注解:

@Configuration
public class CassandraConfig extends AbstractCassandraConfiguration {

    @Override
    protected String getKeyspaceName() {
        return "testKeySpace";
    }

    @Bean
    public CassandraClusterFactoryBean cluster() {
        CassandraClusterFactoryBean cluster = 
          new CassandraClusterFactoryBean();
        cluster.setContactPoints("127.0.0.1");
        cluster.setPort(9142);
        return cluster;
    }

    @Bean
    public CassandraMappingContext cassandraMapping() 
      throws ClassNotFoundException {
        return new BasicCassandraMappingContext();
    }
}

Notice the new bean – BasicCassandraMappingContext – with a default implementation. This is required to map the persistent entities between their object and their persistent formats.

注意到新的bean – BasicCassandraMappingContext – 有一个默认的实现。这对于在其对象和其持久化格式之间映射持久化实体是必需的。

And as the default implementation is capable enough, we can use it directly.

而且,由于默认实现的能力足够,我们可以直接使用它。

3.2. Main Config (Spring Boot)

3.2.主配置(Spring Boot)

Let’s do the Cassandra configuration via application.properties:

让我们通过application.properties进行Cassandra的配置:

spring.data.cassandra.keyspace-name=testKeySpace
spring.data.cassandra.port=9142
spring.data.cassandra.contact-points=127.0.0.1

And we are done! That’s all we need when using Spring Boot.

这样我们就完成了!这就是我们在使用Spring Boot时需要的一切。

3.3. Cassandra Connection Properties

3.3.Cassandra连接属性

There are three mandatory settings we have to configure to setup the connection for a Cassandra client.

为了设置Cassandra客户端的连接,我们必须要配置三个强制性的设置。

We have to setup the host name that the Cassandra server running as contactPoints. Port is simply the listening port for request in server. KeyspaceName is the namespace that defines the data replication on nodes, which is based on a Cassandra related concept.

我们必须设置Cassandra服务器运行的主机名为contactPoints。Port是服务器中请求的监听端口。KeyspaceName是定义节点上数据复制的命名空间,它是基于Cassandra相关的概念。

4. The Cassandra Repository

4.Cassandra存储库

We’re going to use a CassandraRepository for data access layer. This follows the Spring Data repository abstraction, which is focused on abstracting out the code required to implement the data access layers across different persistence mechanisms.

我们将使用CassandraRepository作为数据访问层。这遵循了Spring Data仓库的抽象,其重点是抽象出在不同持久化机制中实现数据访问层所需的代码。

4.1. Create the CassandraRepository

4.1.创建CassandraRepository

Let’s create the CassandraRepository to be used in the configuration:

我们来创建配置中使用的CassandraRepository

@Repository
public interface BookRepository extends CassandraRepository<Book> {
    //
}

4.2. Configuration for CassandraRepository

4.2.CassandraRepository的配置

Now, we can extend the configuration in Section 3.1, adding @EnableCassandraRepositories class level annotation to mark our Cassandra Repository created in section 4.1 in CassandraConfig:

现在,我们可以扩展第3.1节中的配置,添加@EnableCassandraRepositories类级注解,以标记我们在第4.1节中创建的Cassandra Repository在CassandraConfig:中。

@Configuration
@EnableCassandraRepositories(
  basePackages = "com.baeldung.spring.data.cassandra.repository")
public class CassandraConfig extends AbstractCassandraConfiguration {
    //
}

5. The Entity

5.实体

Let’s have a quick look at the entity – the model class we’re going to be using. The class is annotated and defines additional parameters for the metadata Cassandra data table creation in embedded mode.

让我们快速浏览一下实体–我们将要使用的模型类。该类有注释,并定义了在嵌入式模式下创建元数据Cassandra数据表的额外参数。

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:

使用@Table annotation,Bean被直接映射到Cassandra数据表。同时,每个属性都被定义为主键或简单列的类型:

@Table
public class Book {
    @PrimaryKeyColumn(
      name = "isbn", 
      ordinal = 2, 
      type = PrimaryKeyType.CLUSTERED, 
      ordering = Ordering.DESCENDING)
    private UUID id;
    @PrimaryKeyColumn(
      name = "title", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
    private String title;
    @PrimaryKeyColumn(
      name = "publisher", ordinal = 1, type = PrimaryKeyType.PARTITIONED)
    private String publisher;
    @Column
    private Set<String> tags = new HashSet<>();
    // standard getters and setters
}

6. Testing with an Embedded Server

6.使用嵌入式服务器进行测试</b

6.1. Maven Dependencies

6.1.Maven的依赖性

If you want to run Cassandra in embedded mode (without manually installing a separate Cassandra server), you need to add the cassandra-unit related dependencies to the pom.xml:

如果你想以嵌入式模式运行 Cassandra(无需手动安装单独的 Cassandra 服务器),你需要将 cassandra-unit 相关的依赖项添加到 pom.xml

<dependency>
    <groupId>org.cassandraunit</groupId>
    <artifactId>cassandra-unit-spring</artifactId>
    <version>2.1.9.2</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
        <groupId>org.cassandraunit</groupId>
        <artifactId>cassandra-unit</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.cassandraunit</groupId>
    <artifactId>cassandra-unit-shaded</artifactId>
    <version>2.1.9.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.hectorclient</groupId>
    <artifactId>hector-core</artifactId>
    <version>2.0-0</version>
</dependency>

It is possible to use an embedded Cassandra server to test this application. The main advantage is that you don’t want to install Cassandra explicitly.

可以使用嵌入式Cassandra服务器来测试这个应用程序。主要的优点是,你不想明确地安装Cassandra。

This embedded server is compatible with Spring JUnit Tests as well. Here we can set SpringJUnit4ClassRunner using @RunWith annotation together with the embedded server. So it is possible to implement a complete test suite without having an external Cassandra service running.

这个嵌入式服务器也与Spring JUnit测试兼容。在这里,我们可以使用@RunWith注解来设置SpringJUnit4ClassRunner和嵌入式服务器。因此,无需运行外部的Cassandra服务就可以实现一个完整的测试套件。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CassandraConfig.class)
public class BookRepositoryIntegrationTest {
    //
}

6.2. Starting and Stopping the Server

6.2.启动和停止服务器

You can ignore this section if you are running an external Cassandra server.

如果你正在运行一个外部Cassandra服务器,你可以忽略这一部分。

We have to start the server once for the whole test suite, so server start method is marked with @BeforeClass annotation:

我们必须为整个测试套件启动一次服务器,所以服务器启动方法被标记为@BeforeClass注释。

@BeforeClass
public static void startCassandraEmbedded() { 
    EmbeddedCassandraServerHelper.startEmbeddedCassandra(); 
    Cluster cluster = Cluster.builder()
      .addContactPoints("127.0.0.1").withPort(9142).build();
    Session session = cluster.connect(); 
}

Next we have to make sure the server is stopped after the completion of the test suite execution:

接下来,我们必须确保服务器在测试套件执行完成后被停止。

@AfterClass
public static void stopCassandraEmbedded() {
    EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}

6.3. Clean Data Table

6.3.清洁数据表

It is good practice to drop and create the data table before every test execution, to avoid unexpected results due to the manipulated data in earlier test executions.

在每次测试执行之前,放弃并创建数据表是一个很好的做法,以避免由于在早期测试执行中被操纵的数据而产生意外结果。

Now we can create the data table as the server is started:

现在我们可以在服务器启动时创建数据表。

@Before
public void createTable() {
    adminTemplate.createTable(
      true, CqlIdentifier.cqlId(DATA_TABLE_NAME), 
      Book.class, new HashMap<String, Object>());
}

and drop after every single test case execution:

并在每一个测试用例执行完毕后放弃。

@After
public void dropTable() {
    adminTemplate.dropTable(CqlIdentifier.cqlId(DATA_TABLE_NAME));
}

7. Data Access Using CassandraRepository

7.使用CassandraRepository的数据访问

We can directly use the BookRepository we created above to persist, manipulate and fetch the data in Cassandra data base.

我们可以直接使用上面创建的BookRepository来持久化、操作和获取Cassandra数据库中的数据。

7.1. Save a New Book

7.1.保存一本新书

We can save a new book to our book store:

我们可以把一本新书保存在我们的书店里。

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media", 
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));

Then we can check the availability of the inserted book in the database:

然后我们可以在数据库中检查插入的书的可用性。

Iterable<Book> books = bookRepository.findByTitleAndPublisher(
  "Head First Java", "O'Reilly Media");
assertEquals(javaBook.getId(), books.iterator().next().getId());

7.2. Update an Existing Book

7.2.更新现有的书籍

Lat’s start by inserting a new book:

我们从插入一本新书开始。

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media", 
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));

Let’s fetch the book by the title:

让我们按书名取书。

Iterable<Book> books = bookRepository.findByTitleAndPublisher(
  "Head First Java", "O'Reilly Media");

Then let’s change the title of the book:

那么让我们改变书名:

javaBook.setTitle("Head First Java Second Edition");
bookRepository.save(ImmutableSet.of(javaBook));

Finally let’s check the whether the title is updated in the database:

最后,让我们检查一下数据库中的标题是否已经更新。

Iterable<Book> books = bookRepository.findByTitleAndPublisher(
  "Head First Java Second Edition", "O'Reilly Media");
assertEquals(
  javaBook.getTitle(), updateBooks.iterator().next().getTitle());

7.3. Delete the Existing Book

7.3.删除现有的书

Insert a new book:

插入一本新书。

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media",
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));

Then delete the newly entered book:

然后删除新输入的书。

bookRepository.delete(javaBook);

Now we can check for the deletion:

现在我们可以检查是否删除:

Iterable<Book> books = bookRepository.findByTitleAndPublisher(
  "Head First Java", "O'Reilly Media");
assertNotEquals(javaBook.getId(), books.iterator().next().getId());

This will cause to throw a NoSuchElementException from the code making sure the book is deleted.

这将导致从代码中抛出一个NoSuchElementException,以确保该书被删除。

7.4. Find All Books

7.4.查找所有书籍

Insert a new books first:

先插入一个新书。

Book javaBook = new Book(
  UUIDs.timeBased(), "Head First Java", "O'Reilly Media", 
  ImmutableSet.of("Computer", "Software"));
Book dPatternBook = new Book(
  UUIDs.timeBased(), "Head Design Patterns","O'Reilly Media",
  ImmutableSet.of("Computer", "Software"));
bookRepository.save(ImmutableSet.of(javaBook));
bookRepository.save(ImmutableSet.of(dPatternBook));

Find all books:

查找所有书籍。

Iterable<Book> books = bookRepository.findAll();

Then we can check he number of available books in the database:

然后我们可以检查数据库中的可用书籍数量。

int bookCount = 0;
for (Book book : books) bookCount++;
assertEquals(bookCount, 2);

8. Conclusion

8.结论

We went through a basic hands-on introduction to the Cassandra with Spring data using the most common approach using CassandraRepository data access mechanism.

我们通过基本的实践介绍了Cassandra与Spring数据,使用最常见的方法,使用CassandraRepository数据访问机制。

The implementation of the above code snippets and examples can be found in my GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is.

我的GitHub项目中可以找到上述代码片段和示例的实现 – 这是一个基于Eclipse的项目,所以应该很容易导入并按原样运行。