Spring Framework Integration
Spring Data Cassandra
Build enterprise Java applications with familiar Spring patterns!
🍃 Spring Data Cassandra
Integrate Cassandra seamlessly with Spring Boot using familiar Spring Data patterns!
Why Use Spring Data Cassandra?
- 🍃 Spring Integration: Works perfectly with Spring Boot
- 📚 Repository Pattern: Familiar CRUD operations
- 🏗️ Object Mapping: POJOs to Cassandra tables
- 🔍 Query Methods: Derive queries from method names
- ⚡ Reactive Support: Reactive programming with Project Reactor
- 🛠️ Spring Ecosystem: Works with Spring Security, AOP, etc.
Latest Version: 4.x
Part of Spring Data 2023.x release train
Supports Spring Boot 3.x and Java 17+
📦 Setup & Dependencies
1
Maven Dependencies
<!-- pom.xml -->
<dependencies>
<!-- Spring Data Cassandra -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<!-- Optional: Reactive support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
</dependencies>
2
Gradle Dependencies
// build.gradle
dependencies {
// Spring Data Cassandra
implementation 'org.springframework.boot:spring-boot-starter-data-cassandra'
// Optional: Reactive support
implementation 'org.springframework.boot:spring-boot-starter-data-cassandra-reactive'
}
⚙️ Configuration
application.properties
# Basic configuration
spring.cassandra.keyspace-name=myapp
spring.cassandra.contact-points=127.0.0.1:9042
spring.cassandra.local-datacenter=datacenter1
# Authentication
spring.cassandra.username=cassandra
spring.cassandra.password=cassandra
# Schema management
spring.cassandra.schema-action=create_if_not_exists
# Connection pool
spring.cassandra.pool.max-requests-per-connection=1024
Java Configuration
@Configuration
@EnableCassandraRepositories
public class CassandraConfig extends AbstractCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "myapp";
}
@Override
protected String getContactPoints() {
return "127.0.0.1";
}
@Override
protected int getPort() {
return 9042;
}
@Override
protected String getLocalDataCenter() {
return "datacenter1";
}
// Optional: Custom CqlSession
@Bean
public CqlSessionFactoryBean session() {
CqlSessionFactoryBean session = new CqlSessionFactoryBean();
session.setContactPoints(getContactPoints());
session.setPort(getPort());
session.setKeyspaceName(getKeyspaceName());
session.setLocalDatacenter(getLocalDataCenter());
return session;
}
}
🏗️ Entity Mapping
Simple Entity
import org.springframework.data.cassandra.core.mapping.*;
@Table("users")
public class User {
@PrimaryKey
private UUID id;
private String name;
private String email;
private Integer age;
// Constructors, getters, setters
public User() {}
public User(UUID id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and setters...
}
Composite Primary Key
// Primary Key class
@PrimaryKeyClass
public class PostKey implements Serializable {
@PrimaryKeyColumn(name = "user_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private UUID userId;
@PrimaryKeyColumn(name = "created_at", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
private LocalDateTime createdAt;
// Constructors, getters, setters, equals, hashCode
}
// Entity class
@Table("posts")
public class Post {
@PrimaryKey
private PostKey key;
private String title;
private String content;
// Constructors, getters, setters
}
Collections & UDTs
@Table("users")
public class User {
@PrimaryKey
private UUID id;
private String name;
// List
private List<String> emails;
// Set
private Set<String> tags;
// Map
private Map<String, String> attributes;
// User Defined Type
@Column("address")
private Address address;
}
// User Defined Type
@UserDefinedType("address")
public class Address {
private String street;
private String city;
private String zipCode;
}
📚 Repository Pattern
Basic Repository
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends CassandraRepository<User, UUID> {
// Basic CRUD operations inherited
// - save()
// - findById()
// - findAll()
// - delete()
// - count()
}
Query Methods
@Repository
public interface UserRepository extends CassandraRepository<User, UUID> {
// Find by name
List<User> findByName(String name);
// Find by email
Optional<User> findByEmail(String email);
// Find by age range
List<User> findByAgeBetween(Integer start, Integer end);
// Find by name containing
List<User> findByNameContaining(String keyword);
// Count by name
Long countByName(String name);
// Delete by email
void deleteByEmail(String email);
}
Custom Queries with @Query
import org.springframework.data.cassandra.repository.Query;
@Repository
public interface UserRepository extends CassandraRepository<User, UUID> {
// Custom CQL query
@Query("SELECT * FROM users WHERE name = ?0 ALLOW FILTERING")
List<User> findUsersByName(String name);
// Query with multiple parameters
@Query("SELECT * FROM users WHERE name = :name AND age > :age ALLOW FILTERING")
List<User> findByNameAndAgeGreaterThan(
@Param("name") String name,
@Param("age") Integer age
);
// Update query
@Query("UPDATE users SET email = :email WHERE id = :id")
void updateEmail(@Param("id") UUID id, @Param("email") String email);
}
📝 CRUD Operations
Service Layer
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// CREATE
public User createUser(String name, String email) {
User user = new User();
user.setId(UUID.randomUUID());
user.setName(name);
user.setEmail(email);
return userRepository.save(user);
}
// READ
public Optional<User> getUserById(UUID id) {
return userRepository.findById(id);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
// UPDATE
public User updateUser(UUID id, String newEmail) {
Optional<User> userOpt = userRepository.findById(id);
if (userOpt.isPresent()) {
User user = userOpt.get();
user.setEmail(newEmail);
return userRepository.save(user);
}
throw new RuntimeException("User not found");
}
// DELETE
public void deleteUser(UUID id) {
userRepository.deleteById(id);
}
}
REST Controller
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// GET /api/users
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
// GET /api/users/{id}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable UUID id) {
return userService.getUserById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST /api/users
@PostMapping
public User createUser(@RequestBody UserRequest request) {
return userService.createUser(request.getName(), request.getEmail());
}
// PUT /api/users/{id}
@PutMapping("/{id}")
public User updateUser(
@PathVariable UUID id,
@RequestBody UserRequest request
) {
return userService.updateUser(id, request.getEmail());
}
// DELETE /api/users/{id}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable UUID id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
🔍 Advanced Queries
CassandraTemplate
Low-level query API
@Autowired
CassandraTemplate template;
// Select query
Select select = QueryBuilder
.selectFrom("users")
.all()
.whereColumn("name")
.isEqualTo(literal("Alice"));
List<User> users =
template.select(select, User.class);
Paging & Slicing
Paginated results
import org.springframework.data.domain.*;
// In repository
Slice<User> findByName(
String name,
Pageable pageable
);
// Usage
Pageable page = PageRequest.of(0, 20);
Slice<User> users =
repo.findByName("Alice", page);
Reactive Repositories
Non-blocking queries
public interface ReactiveUserRepo
extends ReactiveCassandraRepository<User, UUID> {
Mono<User> findByEmail(String email);
Flux<User> findByName(String name);
}
Batch Operations
Multiple operations
@Autowired
CassandraTemplate template;
// Batch insert
List<User> users = /*...*/;
template.batchOps()
.insert(users)
.execute();
💡 Best Practices
Configuration Best Practices
- ✅ Use connection pooling: Configure appropriate pool sizes
- ✅ Set timeouts: Prevent hanging operations
- ✅ Enable metrics: Monitor performance
- ✅ Schema management: Use
CREATE_IF_NOT_EXISTSin dev only - ⚠️ Production: Manage schema externally with migrations
Query Optimization
- ✅ Partition key queries: Always query by partition key when possible
- ✅ Avoid ALLOW FILTERING: Indicates poor data model
- ✅ Use projections: Select only needed columns
- ✅ Batch wisely: Only batch same partition key
- ✅ Prepared statements: Spring Data uses them automatically
Common Pitfalls
- ❌ No @PrimaryKey: Every entity needs a primary key
- ❌ Wrong key order: Partition key must be ordinal 0
- ❌ Missing @Table: Table name defaults to class name (lowercase)
- ❌ Using JPA annotations: Use Spring Data Cassandra annotations
- ❌ Transactions: Cassandra doesn't support ACID transactions
Complete Application Example
// Application.java
@SpringBootApplication
public class CassandraApplication {
public static void main(String[] args) {
SpringApplication.run(CassandraApplication.class, args);
}
}
// Entity
@Table("users")
public class User {
@PrimaryKey
private UUID id;
private String name;
private String email;
// getters, setters
}
// Repository
@Repository
public interface UserRepository
extends CassandraRepository<User, UUID> {
Optional<User> findByEmail(String email);
}
// Service
@Service
public class UserService {
@Autowired
private UserRepository repo;
public User save(User user) {
return repo.save(user);
}
}
// Controller
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService service;
@PostMapping
public User create(@RequestBody User user) {
user.setId(UUID.randomUUID());
return service.save(user);
}
}
🎉 Spring Data Cassandra Mastery!
You're now ready to build Spring applications with Cassandra!
🚀 Quick Start:
- ✅ Add
spring-boot-starter-data-cassandradependency - ✅ Configure connection in
application.properties - ✅ Create entity classes with
@Tableand@PrimaryKey - ✅ Define repository interfaces extending
CassandraRepository - ✅ Inject repositories into services
- ✅ Build REST controllers
- ✅ Run and enjoy! 🎉
💡 Key Benefits:
- 🍃 Familiar Patterns: Use Spring Data conventions
- 📚 Repository CRUD: Built-in operations
- 🔍 Query Methods: Derive from method names
- 🏗️ Object Mapping: POJOs to tables automatically
- ⚡ Performance: Connection pooling and prepared statements
- 🔄 Reactive: Optional reactive programming support
🍃 Build enterprise apps with Spring + Cassandra! 🚀
Advertisement
📱 Responsive Ad 📱