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_EXISTS in 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:

  1. ✅ Add spring-boot-starter-data-cassandra dependency
  2. ✅ Configure connection in application.properties
  3. ✅ Create entity classes with @Table and @PrimaryKey
  4. ✅ Define repository interfaces extending CassandraRepository
  5. ✅ Inject repositories into services
  6. ✅ Build REST controllers
  7. ✅ 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 📱