일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- springdatajpa
- 자바
- kotlin
- SpringBoot
- 알고리즘
- 인프런
- QueryDSL
- 백준
- pointcut
- spring
- http
- Servlet
- AOP
- Exception
- Proxy
- JDBC
- 그리디
- jpa
- Greedy
- java
- db
- Thymeleaf
- transaction
- 스프링
- Android
- JPQL
- 스프링 핵심 원리
- 스프링 핵심 기능
- Spring Boot
- 김영한
- Today
- Total
개발자되기 프로젝트
Entity Listener : 2 본문
히스토리 데이터의 경우 db의 특정 데이터가 수정이 되면 해당 값의 복사본은 다른 테이블에 복사해 두는 경우가 있음.
이전에 만든 user에 대한 정보는 중요한 정보로, 데이터의 수정이 이뤄졌을 때 수정된 내역의 히스토리가 필요하닫.
1.UserHistory class
User의 수정 History 정보를 담을 클래스를 작성하자.
package com.jpa.bookmanager.domain;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.LocalDateTime;
@Entity
@NoArgsConstructor
@Data
public class UserHistory {
@Id
@GeneratedValue
private long id;
private Long userId;
private String name;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
2. UserHistoryRepository
package com.jpa.bookmanager.repository;
import com.jpa.bookmanager.domain.UserHistory;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserHistoryRepository extends JpaRepository<UserHistory, Long> {
}
3. UserHistoryListener class
package com.jpa.bookmanager.domain;
import com.jpa.bookmanager.repository.UserHistoryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.PreUpdate;
@Component
public class UserEntityListener {
@Autowired
private UserHistoryRepository userHistoryRepository;
@PreUpdate
public void preUpdate(Object o){
User user = (User) o;
UserHistory userHistory = new UserHistory();
userHistory.setUserId(user.getId());
userHistory.setName(user.getName());
userHistory.setEmail(user.getEmail());
userHistoryRepository.save(userHistory);
}
}
@Autowired를 사용하려면 class가 @Componant지정하여 Bean을 주입받아야 한다.
4. User class에 EnttityListeners수정
UserEntiryListener를 사용하기 위해 value에 추가해 주자.
해당 Listener가 User에 수정이 있을 경우 감시를 하다가 PreUpdate관련된 메서드를 실행할 예정.
@EntityListeners(value = {MyEntityListener.class, UserEntityListener.class})
5. Test
@Test
void userHistoryTest(){
User user = new User();
user.setEmail("hyun19@navernaver.com");
user.setName("hyun19");
userRepository.save(user);
user.setName("hyun20_new");
userRepository.save(user);
userHistoryRepository.findAll().forEach(System.out::println);
}
6. 결과
Caused by: java.lang.NullPointerException
at com.jpa.bookmanager.domain.UserEntityListener.preUpdate(UserEntityListener.java:23)
짠! preUpdate에서 NullPointException 이 발생했다. 해당 method에서 nullpointexception이 발생할 수 있는 경우는
userHistoryRepository가 Null인 경우이다.
여기서 중요한 사실, Entity Listener는 Spring bean을 주입받지 못한다.
따라서 정상적으로 작동을 하지 않아 userHistoryRepository가 Null이 된 것.
7. BeanUtils class생성
따라서 Spring Bean을 가져올 수 있는 특별한 class가 별도로 필요하다.
package com.jpa.bookmanager.support;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component //bean으로 등록
public class BeanUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanUtils.applicationContext = applicationContext;
//applicationContext를 주입받음
}
public static <T> T getBean(Class<T> clazz){
return applicationContext.getBean(clazz);
//clazz에 있는 빈을 리턴
}
}
* Bean : 스프링에서 관리하는 객체
* Application context : spring bean을 생성하고 관리
즉 BeanUtils를 통해 application context를 주입을 받고,
application context에서 내가 원하는 class의 bean을 가져올 수 있다.
8. UserEntityListener 수정
1) @Autowired로 UserHistporyRepository를 연결하지 않고
2) preUpdate() 실행 시 UserHistoryRepository에 BeanUtils를 통해서 bean을 주입해준다.
package com.jpa.bookmanager.domain;
import com.jpa.bookmanager.repository.UserHistoryRepository;
import com.jpa.bookmanager.support.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.PreUpdate;
//@Component //bean으로 등록
public class UserEntityListener {
// @Autowired
//private UserHistoryRepository userHistoryRepository;
@PreUpdate
public void preUpdate(Object o){
UserHistoryRepository userHistoryRepository = BeanUtils.getBean(UserHistoryRepository.class);
User user = (User) o;
UserHistory userHistory = new UserHistory();
userHistory.setUserId(user.getId());
userHistory.setName(user.getName());
userHistory.setEmail(user.getEmail());
userHistoryRepository.save(userHistory);
}
}
9.결과2
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
user
(created_at, email, gender, name, updated_at, id)
values
(?, ?, ?, ?, ?, ?)
Hibernate:
select
user0_.id as id1_1_0_,
user0_.created_at as created_2_1_0_,
user0_.email as email3_1_0_,
user0_.gender as gender4_1_0_,
user0_.name as name5_1_0_,
user0_.updated_at as updated_6_1_0_
from
user user0_
where
user0_.id=?
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
user_history
(created_at, email, name, updated_at, user_id, id)
values
(?, ?, ?, ?, ?, ?)
Hibernate:
update
user
set
created_at=?,
email=?,
gender=?,
name=?,
updated_at=?
where
id=?
Hibernate:
select
userhistor0_.id as id1_2_,
userhistor0_.created_at as created_2_2_,
userhistor0_.email as email3_2_,
userhistor0_.name as name4_2_,
userhistor0_.updated_at as updated_5_2_,
userhistor0_.user_id as user_id6_2_
from
user_history userhistor0_
UserHistory(id=7, userId=6, name=hyun20_new,
email=hyun19@navernaver.com, createdAt=null, updatedAt=null)
10. 최종
1) 생성/수정시간 입력을 위해 UserHistory가 Autitable을 구현하도록 한다.
@EntityListeners(value = MyEntityListener.class)
public class UserHistory implements Auditable{
2) UserEntityListener가 prePersist, preUpdate에서 동작하도록 한다.
@PrePersist
@PreUpdate
public void prePersistpreUpdate(Object o){
3) 결과
Hibernate:
call next value for hibernate_sequence
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
user_history
(created_at, email, name, updated_at, user_id, id)
values
(?, ?, ?, ?, ?, ?)
Hibernate:
insert
into
user
(created_at, email, gender, name, updated_at, id)
values
(?, ?, ?, ?, ?, ?)
Hibernate:
select
user0_.id as id1_1_0_,
user0_.created_at as created_2_1_0_,
user0_.email as email3_1_0_,
user0_.gender as gender4_1_0_,
user0_.name as name5_1_0_,
user0_.updated_at as updated_6_1_0_
from
user user0_
where
user0_.id=?
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
user_history
(created_at, email, name, updated_at, user_id, id)
values
(?, ?, ?, ?, ?, ?)
Hibernate:
update
user
set
created_at=?,
email=?,
gender=?,
name=?,
updated_at=?
where
id=?
Hibernate:
select
userhistor0_.id as id1_2_,
userhistor0_.created_at as created_2_2_,
userhistor0_.email as email3_2_,
userhistor0_.name as name4_2_,
userhistor0_.updated_at as updated_5_2_,
userhistor0_.user_id as user_id6_2_
from
user_history userhistor0_
UserHistory(id=6, userId=null, name=hyun19, email=hyun19@navernaver.com,
createdAt=2021-06-16T22:55:06.430384, updatedAt=2021-06-16T22:55:06.430384)
UserHistory(id=8, userId=7, name=hyun20_new, email=hyun19@navernaver.com,
createdAt=2021-06-16T22:55:06.576311, updatedAt=2021-06-16T22:55:06.576311)
user의 정보가 생성/수정 될 때 userHistory에 그 정보가 기록되는것을 확인이 가능하다.
'JPA' 카테고리의 다른 글
Entity Listener : 4(실제 사용하는 방법) (0) | 2021.06.16 |
---|---|
Entity Listener : 3(@AuditingEntityListener) (0) | 2021.06.16 |
Entity Listener - 1 (0) | 2021.06.15 |
@Entity 속성 2 (0) | 2021.06.15 |
@Entity 기본 속성 - 1 (0) | 2021.06.15 |