일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 알고리즘
- CI
- Oracle
- cd
- superBuilder
- CI/CD
- IntelliJ
- Di
- Vue
- builder-pattern
- 해시맵
- k8s
- map
- vuejs
- Kubernetes
- programmers
- hibernate
- 자바
- 코딩테스트연습
- java
- docker
- CKA
- ORM
- SpringMVC
- DevOps
- JPA
- dabase
- Spring
- 뷰
- 프로그래머스
Archives
- Today
- Total
문홍의 공부장
[Java] EntityListeners 를 활용하여 엔티티의 생명 주기에 따른 이벤트 처리하기 (JPA Entity Lifecycle Events) 본문
개발/Java
[Java] EntityListeners 를 활용하여 엔티티의 생명 주기에 따른 이벤트 처리하기 (JPA Entity Lifecycle Events)
moonong 2024. 1. 28. 21:53반응형
JPA를 사용하다보면 여러가지 상황을 캐치하여 작업을 해야할 경우가 생기곤 한다.
예를 들어, 데이터의 수정/삭제가 발생하였을 시 그 요청 이력을 관리하여야 한다고 가정하자. 이 때 모든 수정/삭제 로직이 끝날 때 이력 관리 테이블에 데이터를 저장하는 로직을 추가하는 것은 매우 비효율적인 일이다. 하이버네이트에서 제공하는 JPA EntityListeners 를 활용하여 엔티티의 생명 주기에 따른 이벤트를 처리할 수 있다.
Callback Events
- PrePersist:
persist()
를 호출하기 전, 새로운 엔티티가 영속성 컨텍스트에 관리되기 직전에 호출된다.- 식별자 생성 전략을 사용한 경우, 엔티티의 식별자는 아직 존재하지 않는 상태이다.
- PostPersist:
persist()
를 호출한 후,flush()
나commit()
을 통해 엔티티를 데이터베이스에 저장한 직후에 호출된다.- 식별자가 항상 존재한다.
- 생성자 전략이 IDENTITY 인 경우, 식별자 생성을 위해
persist()
호출하여 엔티티를 저장하게 된다. 이때에는persist()
를 호출한 직후에 바로@PostPersist
가 호출된다.
- PreRemove:
remove()
를 호출하여 엔티티를 영속성 컨텍스트에서 삭제하기 직전에 호출된다.orphanRemoval
에 대하여는flush()
나commit()
시에 호출된다.
- PostRemove:
flush()
나commit()
를 호출하여 엔티티를 데이터베이스에서 삭제한 직후에 호출된다. - PreUpdate:
flush()
나commit()
를 호출하여 엔티티를 데이터베이스에서 수정하기 직전에 호출된다. - PostUpdate:
flush()
나commit()
를 호출하여 엔티티를 데이터베이스에서 수정한 직후에 호출된다.persist()
시에는 호출되지 않는다- 변경감지에 의한 업데이트 시에도 호출된다
- PostLoad: 엔티티가 영속성 컨텍스트에 로드(조회 또는
refresh()
)된 후에 호출된다.- 2차 캐시에 저장되어 있어도 호출된다.
@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
...
@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;
}
}
위와 같은 콜백 메서드들은, 엔티티마다 일어나야 하는 작업이 다를 때 유용하게 사용될 수 있다. 하지만 모든 엔티티에 대하여 적용되는 경우는 어떨까? 모든 엔티티에 이력 관리를 위한 콜백 이벤트 메서드를 작성하는 것은 결국 코드를 중복 작성하는 일이다. 또한, 유저 정보와 이력 관리라는 두 가지 역할을 효율적으로 관리하기 위하여서도 분리가 필요해보인다.
이를 이를 전역적으로 관리하기 위해 EntityListeners
를 활용할 수 있다.
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());
}
}
User
테이블의 로그를 관리하기 위한 AuditTrailListener
클래스를 생성하였다. 그리고 유저 엔티티에 아래와 같이 @EntityListener
를 추가한다.
@EntityListeners(AuditTrailListener.class)
@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
}
이로써 유저 엔티티의 모든 활동은 AuditTrailListener
을 통해 관리된다.
References.
반응형
'개발 > Java' 카테고리의 다른 글
[Java] 비동기 작업에 사용되는 CompletableFuture (0) | 2024.03.04 |
---|---|
[Java] Querydsl @QueryProjection, transform() 을 활용하여 효율적으로 Result Handling 하기 (0) | 2024.01.28 |
[Java] Shorten URL (단축 URL) 생성하기 (0) | 2023.07.19 |
[Java] BigDecimal 사용하기 (1) | 2023.06.18 |
헤더에는 왜 언더스코어를 쓰면 안되나요? (feat. 제 로컬에서는 되는데요) (0) | 2023.06.05 |