Triển khai API CRUD với Cassandra và Spring Boot giúp cho việc quản lý dữ liệu trở nên đơn giản và hiệu quả hơn, đồng thời cung cấp khả năng lưu trữ và truy xuất dữ liệu lớn.
Bài hôm nay chúng ta sẽ triển khai một dự án Spring boot nho nhỏ, để demo cách sử dụng Cassandra với Spring.
Cấu trúc dự án cassandra spring boot
Các bạn làm lần lượt theo cấu trúc bên dưới
package com.cafeincode.cassandra.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
import java.util.UUID;
@Table("user")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
@PrimaryKey
private UUID id;
private String name;
private Integer age;
}
package com.cafeincode.cassandra.mapper;
import com.cafeincode.cassandra.entity.User;
import org.mapstruct.*;
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mapping(source = "id", target = "id", ignore = true)
User from(User user);
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
@Mapping(source = "id", target = "id", ignore = true)
void updateFields(User input, @MappingTarget User target);
}
package com.cafeincode.cassandra.repository;
import com.cafeincode.cassandra.entity.User;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface UserRepository extends CassandraRepository<User, UUID> {
}
package com.cafeincode.cassandra.service;
import com.cafeincode.cassandra.entity.User;
import com.cafeincode.cassandra.mapper.UserMapper;
import com.cafeincode.cassandra.repository.UserRepository;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.UUID;
@Service
@AllArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
public User createUser(User request) {
var jpaUser = userMapper.from(request);
jpaUser.setId(Uuids.timeBased());
return userRepository.save(jpaUser);
}
@Transactional(rollbackFor = Exception.class)
public User updateUser(UUID id, User request) {
var jpaUser = userRepository.findById(id);
if (jpaUser.isEmpty()) {
throw new RuntimeException("User find not found");
}
var jpaUpdated = jpaUser.get();
userMapper.updateFields(request, jpaUpdated);
return userRepository.save(jpaUpdated);
}
public User findById(UUID id) {
return userRepository.findById(id).orElseThrow(RuntimeException::new);
}
@Transactional(rollbackFor = Exception.class)
public void deleteById(UUID id) {
userRepository.deleteById(id);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
}
spring.data.cassandra.local-datacenter=datacenter1
spring.data.cassandra.keyspace-name=cafeincode_keyspace
spring.data.cassandra.contact-points=localhost
spring.data.cassandra.port=9042
spring.data.cassandra.request.consistency=quorum
server.port=8888
version: '3.9'
services:
node1:
image: cassandra:4.0
container_name: cassandra-node-cafeincode
restart: always
environment:
- CASSANDRA_CLUSTER_NAME=cassandra-cafeincode-cluster
- CASSANDRA_DC=datacenter1
- CASSANDRA_RACK=rack1
- CASSANDRA_SEEDS=node1
volumes:
- ./cassandra-data/node1:/var/lib/cassandra/data
- ./cassandra-logs/node1:/var/log/cassandra
ports:
- "9042:9042"
- "7000:7000"
- "7001:7001"
- "7199:7199"
- "9160:9160"
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cafeincode</groupId>
<artifactId>cassandra-spring</artifactId>
<version>0.0.1</version>
<name>cassandra-spring-example</name>
<description>Restful API using Cassandra and Spring boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.cafeincode.cassandra.controller;
import com.cafeincode.cassandra.entity.User;
import com.cafeincode.cassandra.service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/api/users")
@AllArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User request) {
var createdUser = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable UUID id, @RequestBody User request) {
var updatedUser = userService.updateUser(id, request);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable UUID id) {
userService.deleteById(id);
return ResponseEntity.noContent().build();
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable UUID id) {
var user = userService.findById(id);
return ResponseEntity.ok(user);
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
var users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
}
Phần Controller mình viết một số api đơn giản, create/update/delete/get detail/get all, phần logic chi tiết các bạn có thể xem ở file UserService
, ngoài ra cuối bài cũng có link source code để các bạn tham khảo thêm.
Tạo key space trong cassandra
Trước tiên phải start Docker lên trước, sau đấy dùng lệnh docker-compose up -d
để triển khai container cassandra
Sau đó vào thẳng container trong Docker Hub để tương tác với cassandra, dùng command cqlsh
để kết nối vào cassandra cluster
Cũng giống như một vài loại cơ sở dữ liệu mà các bạn đã quen thuộc, thì chúng ta phải tạo schema trước rồi mới tạo table sau, đối với cassandra thì key space cũng tương tự như schema vậy.
Mình dùng đoạn lệnh bên dưới để khởi tạo một key space cafeincode_keyspace
create keyspace cafeincode_keyspace with replication={'class':'SimpleStrategy','replication_factor': '1'};
Sau khi tạo xong key space, chúng ta sẽ tạo một bảng user
, các bạn nhớ phải sử dụng đoạn lệnh use + schema thì mới tạo được bảng.
CREATE TABLE user (
id UUID,
name text,
age int,
PRIMARY KEY (id)
);
dùng lệnh describe user
để xem lại thông tin bảng chúng ta đã tạo bên trên
Check lại data trong bảng user chút nào
Kiểm thử
Giờ chúng ta sẽ start dự án lên và gọi thử API từ postman để xem dữ liệu đã được lưu trữ trong cassandra hay chưa
Giờ chúng ta sẽ gọi 3 lần để tạo 3 bản ghi user.
Xong, giờ sẽ vào query trong cassandra xem dữ liệu như thế nào
Vậy là chúng ta đã demo xong việc viết api restful và sử dụng cassandra để lưu trữ dữ liệu.
Các bạn có thể xem triển khai chi tiết tại link sau: cafeincode.com/cassandra
Xem thêm một số bài viết nổi bật:
- Những điều bạn cần biết về Cassandra
- Crack Intellij IDEA Ultimate version 2022
- How to build Cron Job for multiple instances with ShedLock
- Distributed Lock with Hazelcast and Spring
- How to build Rate Limit with Hazelcast and Spring Boot
- Biết sử dụng git cherry-pick để làm việc hiệu quả hơn
- Git revert với Git reset hoạt động như thế nào?
- Git stash giúp bạn trở nên chuyên nghiệp như thế nào
- Học cách lắng nghe tích cực để hiểu người khác hơn
- Kĩ năng quản lý căng thẳng cho Developer
- Câu chuyện phỏng vấn online mùa Covid
- Phỏng vấn dạo kĩ sư phần mềm 2023