0

Tối ưu hóa và sử dụng Native Query với @Transactional trong Spring Boot để lấy danh sách Notification

Trong bài viết này, mình sẽ hướng dẫn cách sử dụng Native SQL Query kết hợp với annotation @Transactional trong Spring Boot để lấy danh sách thông báo (notification) từ cơ sở dữ liệu một cách hiệu quả. Chúng ta sẽ phân tích đoạn code thực tế và tìm hiểu cách tối ưu hóa nó.

  1. Đoạn code gốc Đây là đoạn code sử dụng Native SQL để lấy danh sách thông báo:

java Sao chép Chỉnh sửa @Transactional public List<NotificationTerm> getNotifications(SearchNotificationDto searchNotificationDto, String userId) throws Exception { String sql = "WITH user_notification AS ( " + " SELECT " + " un.notification_id, " + " CASE " + " WHEN un.is_read = 't' THEN 2 " + " ELSE 1 " + " END AS status, " + " un.created_date AS date, " + " un.group_id " + " FROM notification.en_laand_user_notification un " + " WHERE un.user_id = :userId " + "), " + "notification AS ( " + " SELECT " + " n.id AS notification_id, " + " CASE " + " WHEN n.is_global = 't' THEN 1 " + " ELSE 0 " + " END AS status, " + " n.created_date AS date " + " FROM notification.en_laand_notification n " + " WHERE " + " (n.is_deleted = 'f') " + " AND (n.notification_type = :notification_type) " + " AND (n.expired_date IS NULL OR (NOW() BETWEEN n.effective_date AND n.expired_date)) " + " AND (n.is_global = 't' OR n.user_id = :userId) " + " OR EXISTS ( " + " SELECT 1 " + " FROM user_notification un " + " WHERE n.group_id = un.group_id " + " ) " + " AND (n.content IS NULL OR n.content LIKE '%page1%') " + " UNION ALL " + " SELECT " + " un.notification_id, " + " un.status, " + " un.date AS date " + " FROM user_notification un " + ") " + "SELECT " + " notification_id, " + " SUM(status) " + "FROM notification " + "GROUP BY notification_id " + "ORDER BY " + " CASE " + " WHEN SUM(status) > 1 THEN 1 " + " ELSE 0 " + " END, " + " MIN(date) " + "LIMIT :limit OFFSET :offset";

Query query = this.entityManager.createNativeQuery(sql, NotificationTerm.class);

query.setParameter("userId", userId);
query.setParameter("notification_type", 3);
query.setParameter("limit", searchNotificationDto.getSize());
query.setParameter("offset", searchNotificationDto.getSize() * searchNotificationDto.getPage());

return query.getResultList();

} 2. Giải thích đoạn code a. WITH CTE (Common Table Expression)` user_notification: Chứa danh sách thông báo của người dùng dựa trên user_id. notification: Truy vấn kết hợp giữa thông báo toàn cục (is_global = 't') và thông báo của người dùng hoặc nhóm. Điều kiện WHERE quan trọng:

sql Sao chép Chỉnh sửa WHERE (n.is_deleted = 'f') AND (n.notification_type = :notification_type) AND (n.expired_date IS NULL OR (NOW() BETWEEN n.effective_date AND n.expired_date)) AND (n.is_global = 't' OR n.user_id = :userId) Điều kiện tồn tại (EXISTS) giúp lấy các thông báo nếu group_id của người dùng khớp với group_id của thông báo. b. GROUP BY và ORDER BY sql Sao chép Chỉnh sửa GROUP BY notification_id ORDER BY CASE WHEN SUM(status) > 1 THEN 1 ELSE 0 END, MIN(date) Các thông báo được sắp xếp theo mức độ ưu tiên (status) và ngày tạo nhỏ nhất (MIN(date)). c. Phân trang (LIMIT và OFFSET) :limit và :offset giúp giới hạn kết quả trả về theo trang (page) và kích thước trang (size). 3. Những điểm cần tối ưu Sử dụng dấu ngoặc để tránh lỗi logic: Điều kiện AND ... OR cần dấu ngoặc để đảm bảo ưu tiên đúng. Ví dụ:

sql Sao chép Chỉnh sửa AND ((n.is_global = 't' OR n.user_id = :userId) OR EXISTS ( SELECT 1 FROM user_notification un WHERE n.group_id = un.group_id )) Truy vấn dạng DTO thay vì Native SQL khi có thể: Sử dụng JPQL với Constructor Expression sẽ giúp mã dễ đọc hơn và tránh lỗi SQL thuần. Ví dụ:

java Sao chép Chỉnh sửa SELECT new NotificationTerm(n.id, n.createdDate, n.status) FROM Notification n WHERE n.userId = :userId Xử lý phân trang an toàn: Kiểm tra giá trị của :limit và :offset để tránh truyền giá trị không hợp lệ.

  1. Kết luận Đoạn code trên là một ví dụ điển hình về cách sử dụng Native Query với Spring Boot để lấy dữ liệu từ nhiều bảng và thực hiện xử lý dữ liệu phức tạp. CTE (Common Table Expression) giúp cải thiện khả năng đọc và quản lý truy vấn dài dòng. Việc tối ưu hóa và kiểm tra logic WHERE là cần thiết để tránh lỗi không mong muốn.

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí