Ngoài những thành phần cơ bản cho web, thì spring boot cũng hỗ trợ chúng ta tích hợp với các framework về data như Redis. Trong bài này Dũng sẽ cùng các bạn cài đặt và khởi động một dự án đơn giản với spring boot và Redis nhé.

Khởi tạo module

Chúng ta sẽ khởi tạo một module có tên spring-boot-redis.

Chuẩn bị

Để cho đơn giản chúng ta sẽ sử dụng docker để cài đặt redis và khởi động nó thông qua các câu lệnh sau:

docker pull redis
docker run --name=redis -p 6379:6379 -d redis:latest

Một redis server sẽ được khởi tạo và expose cổng 6379.
Lưu ý rằng chúng ta đang sử dụng cho mục đích học tập nên sử dụng docker với Redis, trong môi trường dự án thực tế có thể redis sẽ được cài theo một cách khác sử dụng vps hoặc máy chủ riêng biệt.

Cấu hình dự án

Để có thể sử dụng redis trong spring boot, chúng ta cần thay đổi tập tin spring-boot-redis/pom.xml như sau:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>vn.techmaster</groupId>
        <artifactId>mastering-spring-boot</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>spring-boot-redis</artifactId>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
    </dependencies>
</project>

Ở đây chúng ta sử dụng 2 thư viện mới cho redis là spring-boot-starter-data-redis và jedis và 1 thư viện cho json là spring-boot-starter-json. Cần lưu ý rằng jedis là thư viện của cộng đồng tạo ra chứ không phải là spring.

Tạo lớp RedisConfig

Chúng ta cần tạo ra lớp cấu hình RedisConfig như sau:

package vn.techmaster.redis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
public class RedisConfig {

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        return new JedisConnectionFactory();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory());
        return template;
    }
}

Ở đây chúng ta đang khởi tạo đối JedisConnectionFactory để tạo kết nối với Redis và đối tượng RedisTemplate để đóng gói các hàm tiện ích để giao tiếp với Redis. Chúng ta có thể hiểu RedisTemplate đơn giản là một đối tượng cầu nối với các hàm của thư viện Jedis.

Khởi tạo một repository

Hãy nói chúng ta có một lớp User đại diện cho thông tin người dùng thế này:

package vn.techmaster.redis.data;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class User {
    private String username;
    private String displayName;
}

Chúng ta muốn dùng redis để lưu trữ danh sách người dùng để đảm bảo hiệu năng vì thông tin ngừoi dùng là tính năng thường xuyên được sử dụng trong hệ thống. Chúng ta có thể tạo ra một lớp UserRepository với mã nguồn như sau:

package vn.techmaster.redis.repo;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import vn.techmaster.redis.data.User;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

@Component
@AllArgsConstructor
public class UserRepository {

    private final RedisTemplate<String, Object> redisTemplate;
    private final ObjectMapper objectMapper;

    public void saveUser(User user) {
        redisTemplate.execute((RedisCallback<Object>) connection -> {
            connection.hashCommands().hSet(
                "users".getBytes(StandardCharsets.UTF_8),
                user.getUsername()
                    .getBytes(StandardCharsets.UTF_8),
                serializeUser(user)
            );
            return null;
        });
    }

    public User getUserByUsername(String username) {
        return (User) redisTemplate.execute((RedisCallback<Object>) connection -> {
            byte[] value = connection.hashCommands().hGet(
                "users".getBytes(StandardCharsets.UTF_8),
                username.getBytes(StandardCharsets.UTF_8)
            );
            return deserializeUser(value);
        });
    }

    private byte[] serializeUser(User user) {
        try {
            return objectMapper.writeValueAsBytes(user);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    };

    private User deserializeUser(byte[] value) {
        try {
            return objectMapper.readValue(value, User.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Đây là một lớp tương đối phức tạp vậy nên mình sẽ giải thích một chút.
Về bản chất thì dữ liệu trong máy tính là byte bit và redis cũng không phải là ngoại lệ. Dữ liệu của redis được lưu ở cả trên RAM lẫn trong ổ cứng và ở dạng byte bit, vậy nên chúng ta sẽ cần phải chuyển đổi dữ liệu ở các dạng khác nhau về dạng byte array để redis có thể lưu trữ được.
Thông thường chúng ta sử dụng các thư viện ví dụ như JPA thì chúng ta hay làm việc với đối tượng Java hơn, nó cũng thân thiện hơn do các thư viện đó đã đóng gói hết tất cả các công việc chuyển đối byte bit sang dạng đối tượng java cho chúng ta rồi.
Trong lớp UserRepository chúng ta thực hiện 2 công việc là lưu trữ thông của người dùng và lấy ra thông tin một người dùng theo username.
Về cấu trúc dữ liệu thì chúng ta đang sử dụng dạng hash map để lưu ánh xạ giữa username của người dùng và toàn bộ thông tin của người dùng dưới dạng json byte array. Sử dụng json không phải là một cách tối ưu nhưng cho mục đích học tập chúng ta có thể tạm chấp nhận cách này.

Khởi chạy chương trình

Để khởi chạy chương trình, chúng ta sẽ cần tạo ra lớp RedisStartUp với mã nguồn như sau:

package vn.techmaster.redis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import vn.techmaster.redis.data.User;
import vn.techmaster.redis.repo.UserRepository;

@SpringBootApplication
public class RedisStartUp {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication
            .run(RedisStartUp.class, args);
        UserRepository userRepository = applicationContext
            .getBean(UserRepository.class);
        User user = new User();
        user.setUsername("techmaster");
        user.setDisplayName("Tech Master");
        userRepository.saveUser(user);

        User fetchedUser = userRepository.getUserByUsername(
            "techmaster"
        );
        System.out.println(fetchedUser);
    }
}

Ở đây chúng ta khởi tạo một đối tượng user với username là techmaster và tên hiển thị là Tech Master và sau đó chúng ta lưu thông tin người dùng này vào redis thông qua đối tượng userRepository.
Khởi chạy chương trình và kết quả chúng ta nhận được là:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.1)

2024-07-03T13:14:14.493+07:00  INFO 3889 --- [           main] vn.techmaster.redis.RedisStartUp         : Starting RedisStartUp using Java 17.0.7 with PID 3889 (/Users/tvd12/Documents/techmaster/mastering-spring-boot/spring-boot-redis/target/classes started by tvd12 in /Users/tvd12/Documents/techmaster/mastering-spring-boot)
2024-07-03T13:14:14.497+07:00  INFO 3889 --- [           main] vn.techmaster.redis.RedisStartUp         : No active profile set, falling back to 1 default profile: "default"
2024-07-03T13:14:15.138+07:00  INFO 3889 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2024-07-03T13:14:15.142+07:00  INFO 3889 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2024-07-03T13:14:15.171+07:00  INFO 3889 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 13 ms. Found 0 Redis repository interfaces.
2024-07-03T13:14:16.065+07:00  INFO 3889 --- [           main] vn.techmaster.redis.RedisStartUp         : Started RedisStartUp in 2.121 seconds (process running for 2.666)
User(username=techmaster, displayName=Tech Master)

Tổng kết

Như vậy chúng ta đã cùng nhau khởi tạo một dự án sử dụng spring boot và redis, sau đó chúng ta đã cài đặt một lớp để tương tác với redis và thử nghiệm thành công việc lưu và lấy ra một thông tin người dùng.


Cám ơn bạn đã quan tâm đến bài viết|video này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a