Cũng không đơn giản

Hiện nay có 2 thư viện java client chính hỗ trợ việc đọc ghi dữ liệu đến Redis đó là Jedis và lettuce. Tuy nhiên cả 2 thư viện này đều mới chỉ ở mức cơ bản, nghĩa là mới chỉ hỗ trợ việc đọc ghi byte array. Nhưng để lập trình được ứng dụng thì cái mà lập trình viên cần dùng đó là các đối tượng Java thuần tuý. Vậy nên chúng ta sẽ cần các bước chuyển đổi như sau:

Đọc ghi với Spring Boot

Để đọc ghi redis với Spring Boot, chúng ta sẽ cần import thư việndata-redis như sau:

  1. Với gradle:
implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.4.4'
  1. Với maven:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.4.4</version>
</dependency>

Spring sử dụng Bridge design pattern kiểu thế này:

Nghĩa là chúng ta sẽ đọc ghi dữ liệu đến redis thông qua đối tượng Repository.

Giả sử chúng ta đang cần quản lý một cửa hàng sách, mỗi cuốn sách sẽ có một id duy nhất, với spring trường hợp này thật đơn giản, chỉ cần khai báo lớp BookRepository thế này:

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
}

Để lưu một cuốn sách đến redis, chúng ta sẽ gọi:

bookRepository.save(book);

Để đọc thông tin một cuốn sách từ redis, chúng ta sẽ gọi:

Book book = bookRepository.findById(bookId)

Mọi chuyện thật đơn giản đúng không mọi người? Nhưng đó mới chỉ là những trường hợp đơn giản. Bây giờ xuất hiện nghiệp vụ đó là một tác giả không thể có 2 cuốn sách trùng tên. Chúng ta sẽ cần tạo ra một lớp đại diện cho key như thế này:

public class BookNameAndAuthorId {
    private String bookName;
    private long authorId;
}

Để tạo ra lớp BookIdByNameAndAuthorIdRepository để truy xuất đến redis, chúng ta sẽ có 2 lựa chọn

Cách 1. Tạo ra lớp BookNameAndAuthorIdBookId để tận dụng cơ chế tự động cài đặt của Spring, source code của chúng ta sẽ thế này:

public class BookNameAndAuthorIdBookId {
    private BookNameAndAuthorId id;
    private long bookId;
}

public class BookIdByNameAndAuthorIdRepository 
    extends CrudRepository<BookNameAndAuthorIdBookId, BookNameAndAuthorId> {
}

Nhưng phương pháp này gặp một nhược điểm rất lớn đó là nó lưu trùng dữ liệu vào redis, nghĩa là trong redis sẽ lưu dạng:

map[BookNameAndAuthorId] = BookNameAndAuthorIdBookId

các bạn có thấy không, nó bị trùng dữ liệu BookNameAndAuthorId, và điều này sẽ gây lãng phí bộ nhớ, vậy chúng ta sẽ sử dụng cách 2.

Cách 2: Tự viết lớp BookIdByNameAndAuthorIdRepository, và chúng ta sẽ có source code thế này:

public class BookIdByNameAndAuthorIdRepository {
    private final ObjectMapper objectMapper;
    private final RedisTemplate redisTemplate;
    private final static String MAP_NAME = "SpringBootRedis.BookIdByNameAndAuthorId";

    public Optional<Long> findById(BookNameAndAuthorId id) {
        try {
            String idString = objectMapper.writeValueAsString(id);
            return Optional.ofNullable(
                (Long)redisTemplate
                    .opsForHash()
                    .get(MAP_NAME, idString)
            );
        }
        catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    public void put(BookNameAndAuthorId id, Long bookId) {
        try {
            String idString = objectMapper.writeValueAsString(id);
            redisTemplate.opsForHash()
                .put(MAP_NAME, idString, bookId);
        }
        catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }
}

Tương đối dài đúng không? Nhưng chúng ta sẽ tiết kiệm được rất nhiều dung lượng bộ nhớ.

Đọc ghi với ezydata-redis

Để đọc ghi redis với EzyData, chúng ta sẽ cần import thư việnezydata-redis như sau:

  1. Với gradle:
implementation 'com.tvd12:ezydata-redis:1.1.6'
  1. Với maven:
<dependency>
  <groupId>com.tvd12</groupId>
  <artifactId>ezydata-redis</artifactId>
  <version>1.1.6</version>
</dependency>

Không giống với Spring, ezydata sử dụng Proxy design pattern để giao tiếp với Redis

Điều đó có nghĩa là bạn sẽ không cần phải sinh ra một đối tượng Repository để làm cầu nối nhưng Spring. Để lưu một cuốn sách bạn sẽ chỉ cần sử dụng đối tượng như java thông thường:

Map<Long, Book> bookMap = redisProxy.getMap("book");
bookMap.put(book.getId(), book);

Để lấy thông tin một cuốn sách bạn cũng gọi hàm get như bình thường:

Book book = bookMap.get(bookId);

Đối với trường hợp key phức tạp như BookNameAndAuthorId, chúng ta cũng chỉ đơn giản tạo lớp:

@EzyCachedKey
public class BookNameAndAuthorId {
    private String bookName;
    private long authorId;
}

Và sử dụng như thế này:

Map<BookNameAndAuthorId, Long> map = redisProxy.getMap(
            "bookIdByNameAndAuthorId",
            BookNameAndAuthorId.class,
            Long.class
        );
map.put(bookNameAndAuthorId, bookId);
Long existedBookId = map.get(bookNameAndAuthorId);

Tổng kết

Để lập trình với redis cũng không phải dễ nếu như chúng ta chỉ sử dụng các thư viện lập trình cơ bản. Nhưng với sự ra đời của các thư viện như Spring Boot hay EzyData mọi thứ đã thật sự dễ dàng. Đặc biệt với EzyData, việc lập trình redis không khác gì với việc chúng ta lập trình với java thông thường.

Tham khảo

  1. Đọc Ghi Redis với Spring Boot
  2. Dọc Ghi Redis với EzyData