JPA(Java Persistence API) | JPA Auditing

JPA Auditing이란?

JPA를 사용하여 Domain을 RDBS의 테이블에 매핑 할 때 공통적으로 Domain을 가지고 있는 필드나 컬럼이 존재한다.

대표적으로 다음과 같다.

  • CreateDate
  • UpdateDate
  • 식별자

위와 같은 같은 필드 및 컬럼이 존재하는데, Domain마다 존재하게 된다면 중복 코드가 많이 발생하게 될 것이다. 이런 필드는 데이터베이스를 누가 언제 작성했는지 등 기록을 남긴 것이 유지 관리에도 도움이 된다. 따라서, 생성 날짜, 수정 날짜 같은 컬럼은 정말 중요한 데이터라고 볼 수 있다.

JPA에서는 그래서 Audit라는 기능을 제공한다. Audit은 감시한다는 의미에서 Spring Data JPA 시간에 자동으로 값을 넣어주는 기능이다. 도메인 지속성 컨텍스트에 저장하고 조회 등을 수행하고 update를 할 경우 매번 시간 데이터를 입력해야 하지만, audit을 이용하여 자동으로 시간을 매핑 데이터베이스의 테이블에 넣어준다.

프로젝트 생성

curl 명령어를 이용하여 spring-boot 프로젝트를 만든다. 이번에는 Dependencies에 “data-jpa,lombok,h2"을 포함한다.

curl https://start.spring.io/starter.tgz  \
-d bootVersion=2.5.0 \
-d dependencies=data-jpa,lombok,h2 \
-d baseDir=spring-jpa-auditing \
-d artifactId=jpa-auditing \
-d packageName=com.devkuma.jpa.auditing \
-d applicationName=Application \
-d packaging=jar \
-d javaVersion=1.8 \
-d type=gradle-project | tar -xzvf -

혹은, 아래 URL를 브라우저에 복사해서 넣고 프로젝트를 생성한다.

https://start.spring.io/#!type=gradle-project&language=java&platformVersion=2.5.0.RELEASE&packaging=jar&jvmVersion=11&groupId=com.devkuma&artifactId=spring-jpa-auditing&name=spring-jpa-auditing&description=Demo%20project%20for%20Spring%20Boot&packageName=com.devkuma.jpa.auditing&dependencies=data-jpa,lombok,h2

소스 코드 구현

BaseTimeEntity 클래스
domain package 내에 BaseTimeEntity 클래스를 만들고, 아래와 같이 작성한다.

package com.devkuma.jpa.auditing.domain;

import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;
}

BaseTimeEntity 클래스는 모든 Entity 클래스의 부모 클래스이며, Entity 클래스의 createdDate, modifiedDate를 자동으로 관리하는 역할을 한다.

  • @MappedSuperclass
    • JPA Entity 클래스가 BaseTimeEntity을 상속하는 경우 필드 (createdDate, modifiedDate)도 열로 인식되도록 한다.
  • @EntityListeners (AuditingEntityListener.class)
    • BaseTimeEntity 클래스에 Auditing 기능을 제공한다.
  • @CreatedDate
    • Entity가 생성되고 저장 될 때 시간이 자동으로 저장된다.
  • @LastModifiedDate
    • 조회 한 Entity의 값을 변경할 때 시간이 자동으로 저장된다.

Entity 클래스에 BaseTimeEntity 클래스를 상속한다.

Post 클래스

package com.devkuma.jpa.auditing.domain;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter
@NoArgsConstructor
@Entity
public class Post extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 500, nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder
    public Post(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }
}
  • @Entity
    • 테이블과 링크되는 클래스를 지정한다.
  • @Id
    • PK 필드를 지정한다.

PostRepository 클래스
JPA Repository를 생성한다.

package com.devkuma.jpa.auditing.repository;

import com.devkuma.jpa.auditing.domain.Post;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post, String> {
}

Application 클래스
마지막으로 JPA Auditing 주석이 활성화되도록 main 클래스에 활성화 주석을 추가한다.

package com.devkuma.jpa.auditing;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

}
  • @EnableJpaAuditing
    • JPA Auditing 주석이 활성화되도록 설정한다.

테스트

테스트는 필수적이므로, 테스트 코드도 작성해 보자.

PostRepositoryTest 클래스
테스크 코드를 test 폴더에 아래와 같이 작성한다.

package com.devkuma.jpa.auditing;

import com.devkuma.jpa.auditing.domain.Post;
import com.devkuma.jpa.auditing.repository.PostRepository;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;
import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;


@SpringBootTest
public class PostRepositoryTest {

    @Autowired
    PostRepository postRepository;

    @Test
    public void 게시글저장() {
        // given
        LocalDateTime now = LocalDateTime.of(2020, 8, 12, 0, 0, 0);
        postRepository.save(Post.builder()
                .title("title")
                .content("content")
                .author("author")
                .build());

        // when
        List<Post> postsList = postRepository.findAll();

        //then
        Post posts = postsList.get(0);

        System.out.println(">>>>>>> createdDate=" + posts.getCreatedDate() + ", modifiedDate=" + posts.getModifiedDate());

        assertThat(posts.getCreatedDate()).isAfter(now);
        assertThat(posts.getModifiedDate()).isAfter(now);
    }
}

결과

... 이하 생략 ...

2021-06-01 09:51:33.635  INFO 71886 --- [    Test worker] c.d.jpa.auditing.PostRepositoryTest      : Started PostRepositoryTest in 7.572 seconds (JVM running for 8.631)
>>>>>>> createdDate=2021-06-01T09:51:33.762582, modifiedDate=2021-06-01T09:51:33.762582
2021-06-01 09:51:34.006  INFO 71886 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'

... 이하 생략 ...

중간 부분에, createdDate, modifiedDate가 지금 시간으로 표시되는 것을 확인할 수 있다.

완성된 코드

위에 모두 완성된 프로젝트 코드는 여기에 있으니 참고 바란다.