This guide demonstrates the three patterns for using DataApiTableCrudRepository with Astra DB Tables in Spring Boot applications.
Use when your table has a single partition key column.
import com.datastax.astra.client.tables.mapping.Column;
import com.datastax.astra.client.tables.mapping.EntityTable;
import com.datastax.astra.client.tables.mapping.PartitionBy;
import java.util.UUID;
@EntityTable("users")
public class User {
@PartitionBy(1)
@Column("user_id")
private UUID userId;
@Column("name")
private String name;
@Column("email")
private String email;
// Constructors, getters, setters
}import com.datastax.astra.spring.DataApiTableCrudRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface UserRepository extends DataApiTableCrudRepository<User, UUID> {
}@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void example() {
// Create
User user = new User(UUID.randomUUID(), "John Doe", "john@example.com");
userRepository.save(user);
// Read
UUID userId = user.getUserId();
Optional<User> found = userRepository.findById(userId);
// Delete
userRepository.deleteById(userId);
}
}Use when your table has multiple partition keys or clustering columns, and you prefer a flexible Map-based approach.
import com.datastax.astra.client.tables.mapping.Column;
import com.datastax.astra.client.tables.mapping.EntityTable;
import com.datastax.astra.client.tables.mapping.PartitionBy;
import com.datastax.astra.client.tables.mapping.PartitionSort;
import com.datastax.astra.client.tables.mapping.PartitionSortOrder;
import java.math.BigDecimal;
import java.time.LocalDate;
@EntityTable("orders")
public class Order {
@PartitionBy(1)
@Column("customer_id")
private String customerId;
@PartitionSort(position = 1, orderBean = PartitionSortOrder.ASC)
@Column("order_date")
private LocalDate orderDate;
@Column("order_id")
private String orderId;
@Column("amount")
private BigDecimal amount;
// Constructors, getters, setters
}import com.datastax.astra.spring.DataApiTableCrudRepository;
import org.springframework.stereotype.Repository;
import java.util.Map;
@Repository
public interface OrderRepository extends DataApiTableCrudRepository<Order, Map<String, Object>> {
}@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public void example() {
// Create
Order orderBean = new Order("CUST123", LocalDate.now(), "ORD001", new BigDecimal("99.99"));
orderRepository.save(orderBean);
// Read - construct primary key as Map
Map<String, Object> primaryKey = Map.of(
"customer_id", "CUST123",
"order_date", LocalDate.now()
);
Optional<Order> found = orderRepository.findById(primaryKey);
// Delete
orderRepository.deleteById(primaryKey);
}
}Use when your table has multiple partition keys or clustering columns, and you want type-safe, object-oriented primary key handling. This pattern is similar to Spring Data Cassandra's @PrimaryKeyClass.
import com.datastax.astra.client.tables.mapping.Column;
import com.datastax.astra.client.tables.mapping.PartitionBy;
import com.datastax.astra.client.tables.mapping.PartitionSort;
import com.datastax.astra.client.tables.mapping.PartitionSortOrder;
import com.datastax.astra.client.tables.mapping.TablePrimaryKeyClass;
import java.time.LocalDate;
import java.util.Objects;
@TablePrimaryKeyClass
public class OrderKey {
@PartitionBy(1)
@Column("customer_id")
private String customerId;
@PartitionSort(position = 1, orderBean = PartitionSortOrder.ASC)
@Column("order_date")
private LocalDate orderDate;
// Default constructor
public OrderKey() {}
// Constructor
public OrderKey(String customerId, LocalDate orderDate) {
this.customerId = customerId;
this.orderDate = orderDate;
}
// Getters and setters
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public LocalDate getOrderDate() { return orderDate; }
public void setOrderDate(LocalDate orderDate) { this.orderDate = orderDate; }
// equals and hashCode are REQUIRED for proper key comparison
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
OrderKey orderKey = (OrderKey) o;
return Objects.equals(customerId, orderKey.customerId) &&
Objects.equals(orderDate, orderKey.orderDate);
}
@Override
public int hashCode() {
return Objects.hash(customerId, orderDate);
}
@Override
public String toString() {
return "OrderKey{customerId='" + customerId + "', orderDate=" + orderDate + "}";
}
}import com.datastax.astra.client.tables.mapping.Column;
import com.datastax.astra.client.tables.mapping.EntityTable;
import com.datastax.astra.client.tables.mapping.TablePrimaryKey;
import java.math.BigDecimal;
@EntityTable("orders")
public class Order {
@TablePrimaryKey
private OrderKey key;
@Column("order_id")
private String orderId;
@Column("amount")
private BigDecimal amount;
@Column("status")
private String status;
// Constructors
public Order() {}
public Order(OrderKey key, String orderId, BigDecimal amount, String status) {
this.key = key;
this.orderId = orderId;
this.amount = amount;
this.status = status;
}
// Getters and setters
public OrderKey getKey() { return key; }
public void setKey(OrderKey key) { this.key = key; }
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
}import com.datastax.astra.spring.DataApiTableCrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface OrderRepository extends DataApiTableCrudRepository<Order, OrderKey> {
}@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public void example() {
// Create - type-safe primary key
OrderKey key = new OrderKey("CUST123", LocalDate.now());
Order orderBean = new Order(key, "ORD001", new BigDecimal("99.99"), "PENDING");
orderRepository.save(orderBean);
// Read - type-safe lookup
Optional<Order> found = orderRepository.findById(key);
// Update
found.ifPresent(o -> {
o.setStatus("COMPLETED");
orderRepository.save(o);
});
// Delete - type-safe deletion
orderRepository.deleteById(key);
// Delete by entity (extracts key automatically)
orderRepository.delete(orderBean);
}
}| Feature | Single Key | Map Key | @TablePrimaryKeyClass |
|---|---|---|---|
| Type Safety | ✅ High | ❌ Low | ✅ High |
| Readability | ✅ Excellent | ✅ Excellent | |
| Refactoring | ✅ Easy | ❌ Difficult | ✅ Easy |
| IDE Support | ✅ Full | ✅ Full | |
| Boilerplate | ✅ Minimal | ✅ Minimal | |
| Best For | Single partition key | Quick prototypes | Production code with composite keys |
- Use Pattern 1 for tables with a single partition key
- Use Pattern 3 (@TablePrimaryKeyClass) for tables with composite keys in production code
- Use Pattern 2 (Map) only for quick prototypes or when key structure is highly dynamic
Add to your application.yml:
astra:
data-api:
token: ${ASTRA_DB_TOKEN}
endpoint-url: ${ASTRA_DB_ENDPOINT}
keyspace: ${ASTRA_DB_KEYSPACE:default_keyspace}All patterns support the full CrudRepository interface:
// Batch operations
List<Order> orders = Arrays.asList(order1, order2, order3);
orderRepository.saveAll(orders);
// Existence check
boolean exists = orderRepository.existsById(key);
// Count
long count = orderRepository.count();
// Find all
Iterable<Order> allOrders = orderRepository.findAll();
// Delete all
orderRepository.deleteAll();