Vòng đời của JPA entity và các event liên quan

29 tháng 03, 2023 - 2348 lượt xem

Học viên: Hứa Lăng Mạnh Quang
Lớp: Java Fullstack 15
Email: mquangdw20@gmail.com
Số điện thoại: 0858422186
Nguồn tham khảo : https://www.baeldung.com/jpa-entity-lifecycle-events?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+Baeldung+(baeldung)

1. Tổng quan

Trong bài viết này, chúng ta sẽ tìm hiểu về các event có thể xử lí trong chu kỳ sống của entity trong JPA và cách sử dụng chúng trong ứng dụng Spring Boot.

2. Các event liên quan tới vòng đời của entity trong JPA

Các event bao gồm:

1.@PrePersist: được gọi trước khi đối tượng được lưu trữ trong cơ sở dữ liệu.
2.@PostPersist: được gọi sau khi đối tượng được lưu trữ trong cơ sở dữ liệu.
3.@PreUpdate: được gọi trước khi đối tượng được cập nhật trong cơ sở dữ liệu.
4.@PostUpdate: được gọi sau khi đối tượng được cập nhật trong cơ sở dữ liệu.
5.@PreRemove: được gọi trước khi đối tượng bị xóa khỏi cơ sở dữ liệu.
6.@PostRemove: được gọi sau khi đối tượng bị xóa khỏi cơ sở dữ liệu.
7.@PostLoad: được gọi sau khi đối tượng được tải từ cơ sở dữ liệu.

Có hai phương pháp để sử dụng các chú thích sự kiện chu kỳ sống:

  • Chú thích phương thức trong đối tượng
  • Tạo một EntityListener với các phương thức callback được chú thích.

Chúng ta cũng có thể sử dụng cả hai đồng thời. Bất kể chúng ở đâu, các phương thức callback yêu cầu phải có kiểu trả về void.

Vì vậy, nếu tạo một đối tượng mới và gọi phương thức lưu của repository, phương thức được chú thích bằng @PrePersist sẽ được gọi, sau đó bản ghi được chèn vào cơ sở dữ liệu và cuối cùng, phương thức của chúng ta được chú thích bằng @PostPersist sẽ được gọi. Nếu sử dụng @GeneratedValue để tự động tạo khóa chính, chúng ta có thể mong đợi rằng khóa đó sẽ có sẵn trong phương thức @PostPersist.

Đối với các hoạt động @PostPersist, @PostRemove và @PostUpdate, tài liệu đề cập đến việc các sự kiện này có thể xảy ra ngay sau khi hoạt động xảy ra, sau một lệnh flush hoặc ở cuối một giao dịch.

Chúng ta cần lưu ý rằng phương thức callback @PreUpdate chỉ được gọi nếu dữ liệu thực sự thay đổi - nghĩa là nếu có một lệnh cập nhật SQL thực sự để chạy. Phương thức callback @PostUpdate được gọi dù cho bất cứ điều gì có thực sự thay đổi hay không.

Nếu bất kỳ phương thức callback nào của chúng ta để lưu trữ hoặc xóa một đối tượng gây ra exception, giao dịch sẽ bị rollbacks.

3. Chú thích thực thể

Chúng ta sẽ bắt đầu bằng cách sử dụng các chú thích callback trực tiếp trong đối tượng. Trong ví dụ ta sẽ để lại một log khi các bản ghi User bị thay đổi, vì vậy ta sẽ thêm các lệnh ghi nhật ký đơn giản trong các phương thức callback.

Ngoài ra, để đảm bảo rằng ta lắp ráp đầy đủ tên của User sau khi họ được tải từ cơ sở dữ liệu. ta sẽ làm điều đó bằng cách chú thích một phương thức với @PostLoad.

Bắt đầu bằng cách định nghĩa đối tượng User:

@Entity
public class User {
    private static Log log = LogFactory.getLog(User.class);

    @Id
    @GeneratedValue
    private int id;
    
    private String userName;
    private String firstName;
    private String lastName;
    @Transient
    private String fullName;

    // Standard getters/setters
}
User.java

Tiếp theo là tạo UserRepository Interface:

public interface UserRepository extends JpaRepository<User, Integer> {
    public User findByUserName(String userName);
}
UserRepository.java

giờ quay lại class User và thêm phương thức call back:

@PrePersist
public void logNewUserAttempt() {
    log.info("Attempting to add new user with username: " + userName);
}
    
@PostPersist
public void logNewUserAdded() {
    log.info("Added user '" + userName + "' with ID: " + id);
}
    
@PreRemove
public void logUserRemovalAttempt() {
    log.info("Attempting to delete user: " + userName);
}
    
@PostRemove
public void logUserRemoval() {
    log.info("Deleted user: " + userName);
}

@PreUpdate
public void logUserUpdateAttempt() {
    log.info("Attempting to update user: " + userName);
}

@PostUpdate
public void logUserUpdate() {
    log.info("Updated user: " + userName);
}

@PostLoad
public void logUserLoad() {
    fullName = firstName + " " + lastName;
}
User.java

Khi chạy các test cases, bạn sẽ thấy một loạt các lệnh ghi nhật ký xuất phát từ các phương thức được chú thích. Ngoài ra, có thể mong đợi đầy đủ tên của người dùng sẽ được điền vào khi tải một người dùng từ cơ sở dữ liệu.

4. Chú thích một Entity Listener

Bây giờ ta sẽ mở rộng ví dụ của mình và sử dụng một EntityListener riêng biệt để xử lý các callback cập nhật. bạn có thể ưa thích phương pháp này hơn là đặt các phương thức trong entity nếu bạn có một số hoạt động muốn áp dụng cho tất cả các entity.

Hãy tạo ra AuditTrailListener để ghi lại tất cả các hoạt động trên bảng User:

public class AuditTrailListener {
    private static Log log = LogFactory.getLog(AuditTrailListener.class);
    
    @PrePersist
    @PreUpdate
    @PreRemove
    private void beforeAnyUpdate(User user) {
        if (user.getId() == 0) {
            log.info("[USER AUDIT] About to add a user");
        } else {
            log.info("[USER AUDIT] About to update/delete user: " + user.getId());
        }
    }
    
    @PostPersist
    @PostUpdate
    @PostRemove
    private void afterAnyUpdate(User user) {
        log.info("[USER AUDIT] add/update/delete complete for user: " + user.getId());
    }
    
    @PostLoad
    private void afterLoad(User user) {
        log.info("[USER AUDIT] user loaded from database: " + user.getId());
    }
}

Theo như ví dụ, chúng ta có thể áp dụng nhiều chú thích vào một phương thức.

Bây giờ, bạn cần quay lại với Entity User và thêm chú thích @EntityListener vào lớp:

@EntityListeners(AuditTrailListener.class)
@Entity
public class User {
    //...
}

Và khi chạy các bài kiểm tra của mình, bạn sẽ nhận được hai bộ thông điệp ghi nhật ký cho mỗi hành động cập nhật và một thông điệp ghi nhật ký sau khi một người dùng được tải từ cơ sở dữ liệu.

5. Tổng Kết

Trong bài viết này, chúng ta đã tìm hiểu về các JPA entity lifecycle callbacks là gì và khi nào chúng được gọi, đã xem xét các chú thích và nói về các quy tắc để sử dụng chúng và cũng đã thực nghiệm sử dụng chúng trong cả một lớp entity và với một lớp EntityListener.
Toàn bộ code mẫu đã được up sẵn tại đây here

Bình luận

avatar
Nguyễn Đức Thắng 2023-03-29 18:34:21.311711 +0000 UTC

hayyy , cảm ơn tác giả

Avatar
* Vui lòng trước khi bình luận.
Ảnh đại diện
  0 Thích
0