JPA의 Auditing 기능을 사용하면 DB에 데이터 입력, 수정 시의 시간, 입력자 정보를 자동으로 관리할 수 있다.
보통 테이블을 생성하면 데이터 입력일자, 수정일자는 기본으로 넣게되는데 jpa에서 이를 자동으로 관리해준다.
@EnableJpaAuditing
먼저 jpa의 auditing 기능을 사용하려면 @EnableJpaAuditing 어노테이션을 사용해야한다. Application.java에 클래스 범위의 어노테이션으로 사용하면 인식 가능하지만, junit 테스트 시 @WebMvcTest와 충돌되는 문제가 있기 때문에 따로 JpaConfig라는 클래스를 생성해서 인식되도록 한다.
JpaConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@Configuration
@EnableJpaAuditing //테스트 진행 시@WebMvcTest와 충돌을 막기위해 JpaConfig 별도 생성
public class JpaConfig {
}
@CreatedDate, @LastModifiedDate
@CreatedDate는 데이터의 최초 입력 시간, @LastModifiedDate는 데이터의 가장 마지막 입력 시간을 관리해준다.
데이터 입력 시간 관리는 여러 테이블에 공통적으로 사용되기 때문에 별도의 클래스로 생성한 뒤 다른 엔티티 클래스에서 상속받을 수 있도록 한다.
@MappedSuperclass 어노테이션을 통해 다른 엔티티 클래스들이 해당 클래스를 상속받았을 때 createdDate, lastModifiedDate 필드들을 컬럼으로 인식하도록 한다.
@EntityListeners 어노테이션을 이용해 클래스에 Auditing 기능을 포함시킨다.
Audit는 감독하고 검사하다는 뜻으로, 해당 데이터를 보고 있다가 생성 또는 수정이 발생하면 자동으로 값을 넣어준다.
BaseTimeEntity.java
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import lombok.Getter;
@Getter
@MappedSuperclass //JPA Enitiy 클래스들이 BaseTimeEntity을 상속할 경우 필드들도 칼럼으로 인식하도록 함.
@EntityListeners(AuditingEntityListener.class) //클래스에 Auditing 기능을 포함시킴
public class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
이후 원하는 엔티티 클래스에서 BaseTimeEntity 클래스를 상속한다.
Member.java
@Getter
@NoArgsConstructor
@Entity
public class Member extends BaseTimeEntity{
...
}
createdDate와 lastModifiedDate는 위의 설정으로 사용 가능하다.
데이터의 생성자, 수정자를 관리하는 @CreatedBy와 @LastModifiedBy는 한단계 더 필요로 하는데, JPA가 생성자, 수정자는 인식할 수 없기 때문에 AuditorAware를 구현해 Bean으로 등록해줘야 한다.
AuditorAware를 implements로 상속한 클래스로 구현해도 되지만 JpaConfig에서 빈으로 등록했다.
우선 테스트를 위해 Long 타입의 랜덤 숫자를 생성하고 테스트를 진행하였다.
JpaConfig.java
import java.util.Optional;
import java.util.Random;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableJpaAuditing //테스트 진행 시@WebMvcTest와 충돌을 막기위해 JpaConfig 별도 생성
public class JpaConfig {
@Bean
public AuditorAware<Long> auditorAware() {
return () -> {
return Optional.of(new Random().nextLong());
};
}
}
테스트 코드
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import com.tlog.backend.global.config.JpaConfig;
import com.tlog.backend.post.domain.Post;
import com.tlog.backend.post.domain.repository.PostRepository;
import com.tlog.backend.post.web.dto.PostRequest;
@ActiveProfiles("test")
@Import(JpaConfig.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class PostRepositoryTest {
@Autowired
PostRepository postRepository;
@Test
@DisplayName("게시물이 저장된다.")
public void save_post_test() {
//given
String title = "post title";
String content = "post content";
Post entity = Post.builder()
.title(title)
.content(content)
.build();
//when
Post savedPost = postRepository.save(entity);
//then
assertThat(savedPost.getTitle()).isEqualTo(title);
assertThat(savedPost.getContent()).isEqualTo(content);
assertThat(savedPost.getCreatedDate()).isNotNull();
assertThat(savedPost.getCreatedBy()).isNotNull();
}
}
이후 헤더로 전달되는 accessToken에서 사용자 id를 추출하는 방식으로 수정.
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import com.tlog.backend.login.JwtProvider;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Configuration
@EnableJpaAuditing //테스트 진행 시@WebMvcTest와 충돌을 막기위해 JpaConfig 별도 생성
public class JpaConfig {
private final HttpServletRequest httpServletRequest;
private final JwtProvider jwtProvider;
@Bean
public AuditorAware<Long> auditorAware() {
return () -> {
String jwtHeader = httpServletRequest.getHeader("Authorization");
String accessToken = jwtProvider.extractAccessToken(jwtHeader);
return Optional.of(jwtProvider.getMemberId(accessToken));
};
}
}
참고 블로그 :
[JPA ] Auditing 기능 살펴보기
Spring Boot 2.4.0 / Spring Data Jpa / JDK 8 / h2database 개요 Auditing 활성화하기 BaseEntity 생성하기 Entity에 적용하기 JUnit으로 테스트해보기 1 @CreatredBy, @ModifiedBy 사용하기 JUnit으로 테스트 해보기 2 실무에서
web-km.tistory.com
https://eoneunal.tistory.com/33
[Spring Data JPA] JPA Auditing을 이용한 생성/수정 이력 추적
0. 들어가며서비스 운영 시 데이터가 생성되고 수정한 이력을 기록하고 트래킹하는 것은 중요하다. Spring Data JPA에서는 Auditing이라는 기능을 제공한다. 이를 통해 엔티티가 생성되고, 변경되는 시
eoneunal.tistory.com
'jpa' 카테고리의 다른 글
테스트용 AuditorAware 별도로 만들기 (0) | 2024.10.15 |
---|---|
saveAll()을 사용하기 위해 List<DTO>를 List<Entity>로 변환 (0) | 2022.11.03 |