Today
-
Yesterday
-
Total
-
  • Spring Boot: Entity Listener
    Spring Boot 🍃 2023. 12. 5. 00:01

    Entity Listener 를 사용하여

    User 테이블에 변경사항이 생길 경우

    해당 히스토리를 저장하는 실습!


    0. Entity Listener

    @Entity Listener

    JPA 엔티티에 대한 이벤트 리스너를 등록하는 어노테이션이다.

    이 어노테이션을 사용하여 엔티티의 생명주기 이벤트를 수신하고 이벤트가 발생할 때 특정 동작을 수행할 수 있다.

    아래 나타날 코드에서는 @EntityListener 어노테이션과, @EntityListener 어노테이션에 할당 할 UserEntityListener 클래스를 작성하고 테스트한다.

    1. User, UserHistory Entity 작성

    User.java

    해당 엔티티가 영속화 될 때 UserHistory 엔티티를 동작시키는 이벤트를 발생시키기 위해 @EntityListeners(value = UserEntityListener.class) 어노테이션을 붙여준다.

    @ToString(callSuper = true)
    @Getter
    @Setter
    @SuperBuilder
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity
    @EntityListeners(value = UserEntityListener.class) // 엔티티 리스너 부착!
    @Table(name = "Users")
    public class User extends BaseEntity {
    
        private String name;
        private String email;
    
    }

    UserHistory.java

    @ToString(callSuper = true)
    @Entity
    @Getter
    @Setter
    @SuperBuilder
    @NoArgsConstructor
    @AllArgsConstructor
    public class UserHistory extends BaseEntity{
    
        @Enumerated(EnumType.STRING) private UserStatus status;
    
        private Long userId;
        private String name;
        private String email;
        @Column(name = "USER_CREATED_AT") private LocalDateTime userCreatedAt;
        @Column(name = "USER_UPDATED_AT") private LocalDateTime userUpdatedAt;
    
    }

    2. EntityListener 작성

    JPA 엔티티의 생명주기에 콜백 메서드를 작성할 수 있게 해주는 어노테이션이 있다.

    1 . JPA 엔티티 생명주기 에서 각각 영속화 되기 전에 반응하는 Pre 어노테이션들

    @PrePersist @PreUpdate @PreRemove


    2 . JPA 엔티티 생명주기 에서 각각 영속화 된 후에 반응하는 Post 어노테이션들

    @PostPersist @PostUpdate @PostRemove


    여기에서 Persist는 create 될 때, Update는 update 될 때, Remove는 delete 될 때 반응한다.


    이 중에서도 실무에서 자주 사용되는 어노테이션은 @PrePersist@PreUpdate 라고 한다.


    이러한 어노테이션을 이용해서

    User 엔티티가 업데이트 될 때

    UserHistory를 업데이트하는 UserEntityListener를 작성해본다.

    public class UserEntityListener {
        private UserHistoryRepository userHistoryRepository;
    
        @PrePersist
        public void prePersist(Object o) {
            save((User) o, UserStatus.CREATE);
        }
        
        @PreUpdate
        public void preUpdate(Object o) {
            save((User) o, UserStatus.UPDATE);
        }
        
        @PreRemove
        public void preRemove(Object o) {
            save((User) o, UserStatus.DELETE);
        }
    
        private void save(User user, UserStatus status) {
    
            if (userHistoryRepository == null)
                userHistoryRepository = BeanUtils.getBean(UserHistoryRepository.class);
    
            userHistoryRepository.save(
                    UserHistory.builder()
                            .status(status)
                            .userId(user.getId())
                            .name(user.getName())
                            .email(user.getEmail())
                            .userCreatedAt(user.getCreatedAt())
                            .userUpdatedAt(user.getUpdatedAt())
                            .build()
            );
    
        }
    }

    EntityListener 객체는 Bean 등록이 불가능해서 , UserHistoryRepository 객체를 주입받을 수 없기 때문에,

    BeanUtil을 만들어서 BeanUtil을 통해 UserHistoryRepository를 가져와 사용한다.

    @Component
    public class BeanUtils implements ApplicationContextAware {
        static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            BeanUtils.applicationContext = applicationContext;
        }
    
        public static <T> T getBean(Class<T> clazz) {
            return applicationContext.getBean(clazz);
        }
    }

    3. test 코드로 기능 동작 확인

    test 코드

    @Test
    void userHistoryTest() {
    
        User user = User.builder()
                .name("zhyun")
                .email("gimwlgus@gmail.com")
                .build();
    
        // new insert
        userRepository.save(user);
    
        // update
        user.setEmail("zhyun@gmail.com");
        userRepository.save(user);
    
        // delete
        userRepository.deleteById(2001L);
    
        // userHistory 데이터 확인
        userHistoryRepository.findAll().forEach(System.out::println);
    
    }

    log 확인

    UserHistory(super=BaseEntity(id=1, createdAt=2023-06-16T00:38:30.273039, updatedAt=2023-06-16T00:38:30.273039), status=CREATE, userId=null, name=zhyun, email=gimwlgus@gmail.com, userCreatedAt=2023-06-16T00:38:30.253169, userUpdatedAt=2023-06-16T00:38:30.253169)
    UserHistory(super=BaseEntity(id=2, createdAt=2023-06-16T00:38:30.429954, updatedAt=2023-06-16T00:38:30.429954), status=UPDATE, userId=2001, name=zhyun, email=zhyun@gmail.com, userCreatedAt=2023-06-16T00:38:30.253169, userUpdatedAt=2023-06-16T00:38:30.429954)
    UserHistory(super=BaseEntity(id=3, createdAt=2023-06-16T00:38:30.454453, updatedAt=2023-06-16T00:38:30.454453), status=DELETE, userId=2001, name=zhyun, email=zhyun@gmail.com, userCreatedAt=2023-06-16T00:38:30.253169, userUpdatedAt=2023-06-16T00:38:30.429954)



    보고 배운 인강 오 링크가 404가 되었다..! 이럴땐 어떡해야 하지; 일단 저장..🙈;

    보고 배운 인강이 판매가 중단된 것 같다. 사이트를 다 뒤져봤는데 안보임!!

    여기서 사용한 코드는 강현호 강사님이 알려주신 내용을 학습한 후에 작성한 코드이다!

Designed by Tistory / Custom by 얼거스