Cassandra with Spring Boot

Spring Boot's Spring Data Cassandra module provides seamless integration between Spring Boot applications and Apache Cassandra. It offers repository pattern support, automatic CQL generation, and object-to-table mapping through annotations — allowing you to work with Cassandra using familiar Spring patterns.

Adding Dependencies

Maven

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>

Gradle

implementation 'org.springframework.boot:spring-boot-starter-data-cassandra'

Configuration (application.yml)

spring:
  cassandra:
    contact-points: 10.0.0.1,10.0.0.2
    port: 9042
    local-datacenter: us-east
    keyspace-name: ecommerce
    username: alice
    password: Al1ce$ecure!
    schema-action: NONE     # use CREATE_IF_NOT_EXISTS for dev
    request:
      consistency: LOCAL_QUORUM
      timeout: 10s
    connection:
      connect-timeout: 10s
      init-query-timeout: 10s

Defining an Entity (Table Mapping)

import org.springframework.data.cassandra.core.mapping.*;
import org.springframework.data.annotation.Id;
import java.math.BigDecimal;
import java.util.UUID;

@Table("products")
public class Product {

  @PrimaryKey
  private UUID productId;

  @Column("name")
  private String name;

  @Column("price")
  private BigDecimal price;

  @Column("category")
  private String category;

  // Constructors, getters, setters...
  public Product(UUID productId, String name, BigDecimal price, String category) {
    this.productId = productId;
    this.name      = name;
    this.price     = price;
    this.category  = category;
  }

  // getters and setters omitted for brevity
}

Composite Primary Key Entity

@PrimaryKeyClass
public class OrderKey implements Serializable {

  @PrimaryKeyColumn(name = "customer_id", ordinal = 0,
                    type = PrimaryKeyType.PARTITIONED)
  private UUID customerId;

  @PrimaryKeyColumn(name = "order_date", ordinal = 1,
                    type = PrimaryKeyType.CLUSTERED,
                    ordering = Ordering.DESCENDING)
  private Instant orderDate;

  @PrimaryKeyColumn(name = "order_id", ordinal = 2,
                    type = PrimaryKeyType.CLUSTERED)
  private UUID orderId;

  // constructors, getters, setters, equals, hashCode...
}

@Table("orders_by_customer")
public class Order {

  @PrimaryKey
  private OrderKey key;

  private String status;
  private BigDecimal total;
  // getters and setters...
}

Creating a Repository

import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.cassandra.repository.Query;
import java.util.List;
import java.util.UUID;

public interface ProductRepository extends CassandraRepository<Product, UUID> {

  // Spring Data generates the CQL automatically:
  List<Product> findByCategory(String category);

  // Custom query:
  @Query("SELECT * FROM products WHERE product_id = ?0")
  Product findProductById(UUID productId);
}

Using the Repository in a Service

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.UUID;

@Service
public class ProductService {

  @Autowired
  private ProductRepository productRepository;

  public Product createProduct(String name, BigDecimal price, String category) {
    Product product = new Product(UUID.randomUUID(), name, price, category);
    return productRepository.save(product);
  }

  public Product getProduct(UUID productId) {
    return productRepository.findById(productId).orElse(null);
  }

  public List<Product> getByCategory(String category) {
    return productRepository.findByCategory(category);
  }

  public void deleteProduct(UUID productId) {
    productRepository.deleteById(productId);
  }
}

Using CassandraTemplate for Advanced Queries

import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Criteria;

@Service
public class OrderService {

  @Autowired
  private CassandraTemplate cassandraTemplate;

  public List<Order> getRecentOrders(UUID customerId) {
    Query query = Query.query(
      Criteria.where("customer_id").is(customerId)
    ).limit(20);

    return cassandraTemplate.select(query, Order.class);
  }

  public void updateOrderStatus(OrderKey key, String newStatus) {
    cassandraTemplate.update(
      Query.query(Criteria.where("customer_id").is(key.getCustomerId())
                          .and("order_date").is(key.getOrderDate())
                          .and("order_id").is(key.getOrderId())),
      org.springframework.data.cassandra.core.query.Update.empty()
        .set("status", newStatus),
      Order.class
    );
  }
}

REST Controller Example

import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/api/products")
public class ProductController {

  @Autowired
  private ProductService productService;

  @GetMapping("/{id}")
  public Product getProduct(@PathVariable UUID id) {
    return productService.getProduct(id);
  }

  @GetMapping("/category/{category}")
  public List<Product> getByCategory(@PathVariable String category) {
    return productService.getByCategory(category);
  }

  @PostMapping
  public Product createProduct(@RequestBody ProductRequest req) {
    return productService.createProduct(
      req.getName(), req.getPrice(), req.getCategory());
  }

  @DeleteMapping("/{id}")
  public void deleteProduct(@PathVariable UUID id) {
    productService.deleteProduct(id);
  }
}

Schema Creation Options

schema-action value     Behavior
──────────────────────────────────────────────────────────────
NONE                    Do not touch schema (use in production)
CREATE_IF_NOT_EXISTS    Create tables if they do not exist (dev)
RECREATE                Drop and recreate tables on startup (dev)
RECREATE_DROP_UNUSED    Recreate + remove tables not in model (dev)

Summary

Spring Data Cassandra integrates Cassandra into Spring Boot applications using familiar repository patterns and JPA-like annotations. Define entities with @Table and @PrimaryKey annotations, create repository interfaces by extending CassandraRepository, and inject them into services. Use CassandraTemplate for complex queries that repositories cannot express. Set schema-action: NONE in production and use migration scripts to control schema changes. Configure consistency levels per-request in application.yml or at the CassandraTemplate level for fine-grained control.

Leave a Comment

Your email address will not be published. Required fields are marked *