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));
}
📌 adminTemplate
是 CassandraAdminOperations
的实例,用于执行 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 数据模型与一致性策略。