YOPLE서비스를 개발하면서 Entity가 저장될 때 또는 조회할 때의 이벤트를 캐치하여 처리를 해야 하는 이슈가 발생했다.
그러던 중 JPA에서 아주 편안한 기능을 제공한다는것을 알게 되었고 포스트로 기록을 남긴다.
JPA에서는 Entity에 이벤트가 발생할 때 특정 처리를 할 수 있게 지원한다.
Entity Listener
Chapter 6. Entity listeners and Callback methods
@PostPersistExecuted after the entity manager persist operation is actually executed or cascaded. This call is invoked after the database INSERT is executed.
docs.jboss.org
위 링크 하이버네이트에서는 JPA Entity에 이벤트가 발생할 때 콜백처리를 할 수 있도록 지원해주는 서비스를 EntityListener라고 정의하였다.
Entity Event??
엔티티 객체의 이벤트라면 "저장", "조회", "수정", "삭제"의 이벤트를 말한다.
이벤트 콜백은 JPA에서 어노테이션으로 지원하며 어노테이션의 Target은 Method다.
- Event 실행 전
@PrePersist
: Entity Save ( Insert )가 실행되기 전@PreUpdate
: Entity Save ( Merge )가 실행되기 전@PreRemove
: Entity Delete가 실행되기 전
- Event 실행 이후.
@PostPersist
: Entity Save ( Insert )가 실행되고 난 후@PostUpdate
: Entity Save ( Merge )가 실행되고 난 후@PostRemove
: Entity Delete가 실행되고 난 후@PostLoad
: Entity Find가 실행되고 난 후
JPA의 사이클 특성상 변수명을 맞추거나 할 필요 없이 Method를 타겟으로하는 어노테이션 기반이므로 메서드명을 자유롭게 작명해도 좋다.
해당 글에서는 위의 Entity Listener가 어떻게 작동되는지 몇가지 예로 알아보자.
@PostLoad
@PostLoad는 find즉 SELECT 이벤트가 실행되고 난 후에 실행되는 콜백이다.
아래는 Entity User의 코드이다.
/**
* Class : User
* Author : RedJunhee
* Description : Class Description
* History : [2022-04-26] - RedJunhee - Class Create
*/
@Entity
@Table(name = "users")
@Getter
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@Column(name="birthdate")
private LocalDateTime dateOfBirth;
@Transient
private int age;
@Column(name = "lastUpdate", columnDefinition = "DATETIME")
private LocalDateTime lastUpdate;
@Column(name = "signUpDate", columnDefinition = "DATETIME")
private LocalDateTime signUpDate;
// 조회 시 자동으로 age 계산 콜백 메서드 정의
@PostLoad
public void calculateAge() {
LocalDateTime now = LocalDateTime.now();
if ( dateOfBirth != null ) {
age = now.minusYears(dateOfBirth.getYear()).getYear();
} else {
age = 0;
}
}
}
calculateAge() 메서드는 @PostLoad 어노테이션으로 find를 하게 되면 age를 계산하는 콜백 메서드이다
실제로 SELECT 조회 시 age가 계산되는지 확인해보자.
User 테이블에는 아래와 같은 데이터가 존재한다.
ID | BIRTHDATE | LAST_UPDATE | NAME | SIGN_UP_DATE |
1 | 2001-01-03 | 2022-04-04 13:45:33.000 | 김아무개 | 1999-05-15 19:33:13.000 |
WebController.Java
import com.map.mutual.side.common.dto.ResponseJsonObject;
import com.map.mutual.side.common.enumerate.ApiStatusCode;
import com.map.mutual.side.common.exception.YOPLEServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
* Class : UserController
* Author : RedJunHee
* Description : Class Description
* History : [2022-04-26] - RedJunHee - Class Create
*/
@RestController(value = "/web")
public class WebController {
@Autowired
private UserService userService;
@GetMapping("/getuser")
public ResponseEntity<ResponseJsonObject> smsAuthenticationRequest(
@RequestParam(value = "id", required = true) Integer id){
try {
User user = userService.getUser(id);
// 계산된 age 출력
System.out.println("user age : " + user.getAge());
}
catch(YOPLEServiceException e){
e.printStackTrace();
throw e;
}
return new ResponseEntity<>(ResponseJsonObject.withStatusCode(ApiStatusCode.OK), HttpStatus.OK);
}
}
UserService.Java
import com.map.mutual.side.common.enumerate.ApiStatusCode;
import com.map.mutual.side.common.exception.YOPLEServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService{
@Autowired
UserRepo userRepo;
public User getUser(Integer id){
// id 유저 SELECT
return userRepo.findById(id)
.orElseThrow(()-> new YOPLEServiceException(ApiStatusCode.SYSTEM_ERROR) );
}
}
UserRepo.Java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepo extends JpaRepository<User, Integer> {
}
자, 준비는 모두 완료되었다.
GET - http://localhost:8080/getuser?id=1 로 실제로 호출해보면 콘솔 창에는 아래와 같이 출력된다.
user age : 21
해당 포스트에서는 @PostLoad만 실제 호출해보았지만 다른 어노테이션도 똑같은 방식으로 사용하면 된다.
이런 이벤트 콜백을 지원해주면서 여러 기능이 가능하다
YOPLE서비스에서는 Entity별로 createDate, updateDate와 같은 필드를 가지는 엔티티가 존재하는데
이런 날짜들은 Entity Event와 빼놓을 수 없는 관계이기에 Listener 기능을 이용해 Entity가 저장될 때, 업데이트될 때
자동적으로 날짜를 바꿔주는 기능으로 많이 사용된다.
'Java > SpringBoot' 카테고리의 다른 글
[Spring Boot] Spring Cloud GateWay 필터 추가 (0) | 2022.06.14 |
---|---|
[Spring Boot] Filter (0) | 2022.06.08 |
[SpringBoot] Exception Handler 예외를 통합관리 하자.!!! (0) | 2022.04.15 |
[SpringBoot] Log4j2 기본 사용법 (0) | 2022.03.07 |
[SpringBoot] SOP, CORS 이야기 (0) | 2022.02.03 |