King of Stock
JPA Auditing 본문
jpa auditing
이 프로젝트는 JPA Auditing 설정하고 테스트 하는 내용을 작성한 프로젝트이다.
사전조건
JPA Auditing 테스트를 하기 위해서 @SpringBootTest 어노테이션을 사용할 것을 권고한다. 그리고 프로젝트는 Gradle 멀티 모듈로 구성되어 있고 본 프로젝트는 루트 프로젝트의 서브 프로젝트이기 때문에 해당 부분에 대해서만 설명한다.
설정
build.gradle
JPA 사용하기 위해서 spring-boot-starter-data-jpa 종속성으로 추가했다.
dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' }
CommonAuditorAware
// ... import, package 중략 ... public class CommonAuditorAware implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { // TODO: 스프링 시큐리티 OAuth2 연동 필요 return Optional.of("SYSTEM"); } }
Optional<String> getCurrentAuditor() method in AuditorAware
Interface인터페이스를 구현하여 Optional Auditor 리턴
JPA Configuration
// ... 중략 ... @EnableJpaAuditing(auditorAwareRef = "commonAuditorAware") @Configuration public class JpaAuditingConfig { @Bean public AuditorAware<String> commonAuditorAware() { return new CommonAuditorAware(); } }
@EnableJpaAuditing(auditorAwareRef = "commonAuditorAware")
JPA Auditing 활성화 하고 현재 자기 자신의 Auditor 찾기 위해 사용하는 BEAN을 지정
return new CommonAuditorAware();
AuditorAware 인터페이스를 구현한 CommonAuditorAware 클래스의 객체를 생성하여 BEAN 만들었다. 이 BEAN에서 Auditor가 제공된다.
abstract class
// ... 중략 ... @MappedSuperclass @EntityListeners(value = { AuditingEntityListener.class }) @Getter public abstract class AbstractAuditEntity { @Column(nullable = false, updatable = false) @CreatedDate private LocalDateTime createdDate; @LastModifiedDate private LocalDateTime lastModifiedDate; @CreatedBy private String createdBy; @LastModifiedBy private String lastModifiedBy; }
@EntityListeners(value = { AuditingEntityListener.class })
엔티티가 영속화 되거나 갱신될때 auditing 정보를 캡처하기 위해 리스너 지정
@CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy
생성시간, 변경시간, 생성자, 변경자 정보를 나타내는 어노테이션을 변수에 지정
Persion class (Entity)
// ... 중략 ... @Entity @NoArgsConstructor @Getter public class Person extends AbstractAuditEntity implements Persistable<UUID> { @Id @GeneratedValue private UUID id; @Setter private String name; @Setter private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } @Override public boolean isNew() { return id == null; } }
특별해 보이지 않는 엔티티 클래스이고 Auditing 관련 설정이 처리된 추상 클래스를 상속 받았다.
테스트
JpaAuditing 처리되는 동작을 테스트 하기 위해서 @SpringBootTest
어노테이션을 사용해서 테스트를 진행했다.
@Slf4j
@SpringBootTest
public class AuditingTests {
@Autowired
private PersonRepository personRepository;
@Test
public void test_jpa_auditing() {
LocalDateTime past = LocalDateTime.now();
Person person = new Person("kuk", 10);
personRepository.save(person);
List<Person> persons = personRepository.findAll();
assertThat(persons).isNotNull();
assertThat(persons.isEmpty()).isFalse();
Person firstMan = persons.get(0);
assertThat(past.isBefore(firstMan.getCreatedDate())).isTrue();
}
@Test
public void test_auto_modified_date() {
Person person = new Person("kuk", 10);
Person saved = personRepository.save(person);
assertThat(saved.isNew()).isFalse();
assertThat(saved.getCreatedBy()).isEqualTo("SYSTEM");
LocalDateTime before = saved.getLastModifiedDate();
log.info("test");
saved.setAge(50);
Person afterSaving = personRepository.save(saved);
log.info("past last modified date: {}", before);
log.info("now last modified date: {}", afterSaving.getLastModifiedDate());
assertThat(before.isBefore(afterSaving.getLastModifiedDate())).isTrue();
}
}
간단하게 엔티티를 저장하고 값을 확인해보는 테스트를 위와 같이 코딩하고 실행했다. 휴...
주의사항 (2019년 8월 10일 Updated)
위에서 예시로 설명한
AbstractAuditEntity
클래스에 @CreatedBy, @LastModifiedBy를 사용하는 변수의 타입이 변경되면 관련 클래스(위에 예시 기준: JpaAuditingConfig, CommonAuditorAware)를 모두 수정해주는걸 잊지 말 것! runtime에만 오류가 발생하고 정확하게 소스 라인이 stacktrace에 보여지지 않기 때문에 찾기가 어려울수 있음.java.lang.ClassCastException: Cannot cast java.lang.String to java.lang.Long at java.lang.invoke.MethodHandleImpl.newClassCastException(MethodHandleImpl.java:361) at java.lang.invoke.MethodHandleImpl.castReference(MethodHandleImpl.java:356) at smartkuk.toy.kubernetes.client.KubernetesClient_Accessor_e5dv52.setProperty(Unknown Source) at org.springframework.data.mapping.PersistentPropertyAccessor.setProperty(PersistentPropertyAccessor.java:78) at org.springframework.data.auditing.MappingAuditableBeanWrapperFactory$MappingMetadataAuditableBeanWrapper.lambda$setProperty$0(MappingAuditableBeanWrapperFactory.java:248) at java.lang.Iterable.forEach(Iterable.java:75)
위에 Stacktrace 는 제네릭 부분을 수정하지 않고 AbstractAuditEntity 클래스의 타입만 변경하고 테스트시 발생한 로그 예시
위에 설명된 프로젝트의 소스코드는 내 github 있다.
'IT 기술' 카테고리의 다른 글
[test] TestRestTemplate (0) | 2019.07.07 |
---|---|
[swagger2] Whitelabel Error Page (0) | 2019.07.06 |
[PostgreSQL] Postgresql DB 생성 on Mac (0) | 2019.04.27 |
[IntelliJ IDE] 인텔리J 구동이 안된 경험 (0) | 2019.04.09 |
[Kubernetes] kubernetes namespace delete (0) | 2019.04.06 |