1. 概述

本文是一篇关于 Spring Data 与 Cassandra 集成的实战入门指南。

我们从基础配置讲起,逐步完成编码,最终搭建一个完整的 Spring Data Cassandra 模块。内容覆盖依赖引入、配置方式、实体定义、Repository 使用以及嵌入式测试等关键环节,适合有经验的 Java 开发者快速上手。

✅ 重点:掌握 Spring Data 抽象层如何简化 Cassandra 操作
⚠️ 踩坑提示:嵌入式测试的端口和启动顺序容易出问题,需特别注意

2. Maven 依赖

首先,在 pom.xml 中引入核心依赖:

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

这是 Cassandra 官方 Java 驱动,Spring Data Cassandra 底层依赖它完成通信。版本选择要与服务端兼容,避免协议不匹配导致连接失败。

3. Cassandra 配置

我们采用 Java Config 方式进行配置,清晰可控。

3.1. 主配置类(Spring)

继承 AbstractCassandraConfiguration 是标准做法,可减少样板代码:

@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();
    }
}

关键点说明:

  • getKeyspaceName() 返回键空间名称,相当于关系型数据库中的“数据库”
  • cluster() 配置连接信息:contactPoints 是集群接口地址(即“接口”),port 是监听端口
  • CassandraMappingContext 负责对象与 CQL 表之间的映射,使用默认实现 BasicCassandraMappingContext 即可

3.2. 主配置类(Spring Boot)

如果你用的是 Spring Boot,配置更简单,直接写入 application.properties

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

无需额外 Java Config,自动装配搞定一切。这就是 Spring Boot 的魅力——约定优于配置。

3.3. 连接参数说明

建立 Cassandra 客户端连接必须配置以下三项:

参数 说明
contactPoints Cassandra 集群的接口地址,如 127.0.0.1
port 监听端口,默认为 9042,示例中使用 9142 属于自定义场景
keyspaceName 键空间名称,决定数据在节点间的复制策略

⚠️ 注意:端口配置错误是常见连接失败原因,务必确认服务端实际监听端口。

4. Cassandra Repository

Spring Data 提供了统一的数据访问抽象层,CassandraRepository 就是其在 Cassandra 上的实现。

4.1. 创建 CassandraRepository

定义接口继承 CassandraRepository

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

这个接口自动具备 save()findAll()delete() 等基础操作,无需手动实现。

4.2. 启用 Repository 扫描

在配置类上添加 @EnableCassandraRepositories 注解,启用 Repository 扫描:

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

这样 Spring 才能识别并注入你定义的 BookRepository。包路径请根据实际项目结构调整。

5. 实体类定义

实体类通过注解映射到 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
}

关键注解解析:

  • @Table:标记该类为 Cassandra 表映射实体
  • @PrimaryKeyColumn
    • type = PARTITIONED:表示该列为分区键(partition key)
    • type = CLUSTERED:表示该列为聚类键(clustering key)
    • ordinal:定义复合主键中的顺序
    • ordering:聚类键排序方式(ASC/DESC)
  • @Column:普通列字段

📌 提示:Cassandra 的主键设计直接影响查询性能和数据分布,合理设计分区键至关重要。

6. 使用嵌入式服务器测试

为了方便单元测试,我们可以使用嵌入式 Cassandra 服务器,无需手动启动外部服务。

6.1. Maven 依赖

添加以下测试依赖:

<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>

cassandra-unit 提供了嵌入式运行能力,配合 Spring 测试上下文使用非常方便。

测试类示例如下:

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

6.2. 启动与停止服务器

在测试类中管理嵌入式服务器生命周期:

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

测试结束后关闭:

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

✅ 建议:@BeforeClass@AfterClass 确保整个测试套件只启停一次,提升效率。

6.3. 清理数据表

每次测试前重建表结构,避免数据污染:

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

测试后删除表:

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

📌 adminTemplateCassandraAdminOperations 的实例,用于执行 DDL 操作。需在测试配置中注入。

7. 使用 CassandraRepository 进行数据操作

通过 BookRepository 可完成 CRUD 操作,简洁高效。

7.1. 保存新书籍

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

验证是否插入成功:

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

⚠️ 注意:save() 方法接受单个实体或集合,示例中原始写法传 ImmutableSet 属于过度包装,直接传对象更清晰。

7.2. 更新已有书籍

先插入:

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

修改标题后再次保存:

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

验证更新:

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

📌 Cassandra 的 save() 实际是 upsert 操作(存在则更新,不存在则插入),无需先查后改。

7.3. 删除书籍

插入后删除:

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

bookRepository.delete(javaBook);

验证删除:

Iterable<Book> books = bookRepository.findByTitleAndPublisher(
  "Head First Java", "O'Reilly Media");
assertFalse(books.iterator().hasNext()); // 应无结果

❌ 原文断言写法有误,assertNotEquals 不适用于判断是否存在,应使用 assertFalse(hasNext())

7.4. 查询所有书籍

批量插入测试数据:

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(javaBook);
bookRepository.save(dPatternBook);

查询全部:

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

统计数量:

long bookCount = StreamSupport.stream(books.spliterator(), false).count();
assertEquals(2, bookCount);

✅ 推荐使用 Java 8 Stream 统计,比手动遍历更简洁安全。

8. 总结

本文带你一步步实现了 Spring Data 与 Cassandra 的集成,涵盖了从配置、实体映射到 CRUD 操作的完整流程。

核心要点回顾:

  • ✅ 使用 CassandraRepository 极大简化数据访问
  • ✅ 嵌入式测试让集成测试更轻量、可移植
  • ✅ 主键设计是 Cassandra 性能的关键,务必重视
  • ✅ Spring Boot 下配置极其简单,推荐生产使用

所有示例代码已托管至 GitHub:https://github.com/example/tutorials/tree/master/persistence-modules/spring-data-cassandra

项目基于 Eclipse 构建,导入即可运行。建议结合官方文档深入理解 Cassandra 数据模型与一致性策略。


原始标题:Introduction to Spring Data Cassandra