티스토리 뷰

 

DB 컬럼에 누가, 언제 작업을 하였는지에 대한 기록을 반드시 남겨야 한다. 그렇지 않는다면 나중에 문제가 발생하였을 때 언제 문제가 발생하였는지 추적하는 게 매우 어렵게 된다. 

 

그렇다면 엔티티를 다음과 같이 설계를 해야할 것 같다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment {

    private String content; // 작성 내용
    private LocalDateTime createdDate; // 작성 시간
    private LocalDateTime lastModifiedDate; // 수정 시간
}

댓글을 등록한 시간, 수정한 시간을 저장하는 필드가 있다. 이것은 좋은 방법이라고 할 수 없다. 왜냐하면 대부분의 필드에 등록 시간과 수정 시간이 필요하기 때문에 매번 필드를 일일이 등록해야 한다. 또한 개발자가 등록 시간과 수정시간을 직접 update 해주어야 한다. 개발자가 실수로 까먹을 수도 있는 부분이다. 

 

JPA에서는 등록 시간과 수정 시간을 자동으로 저장해주는 2가지 방법을 제공한다. 각 방법에 대해서 알아보겠다.

1. 순수한 JPA의 Auditing

순수한 JPA에서 Auditing 기능을 제공한다. 여기서 순수한 JPA란 Spring Data JPA를 사용하지 않은 것을 말한다.

BaseEntity 생성

@MappedSuperclass를 사용한 추상클래스를 생성한다. 클래스 내부에는 등록일과 수정일 필드가 존재한다. 등록일 필드에는 @Column 애너테이션을 사용했는데 이는 등록된 이후 수정되는 것을 방지하기 위해서 사용했다.

@PrePersist : 트랜잭션이 종료되는 시점에 영속성 컨텍스트에 존재하는 엔티티를 flush 하는 시점에 호출된다.

@PreUpdate : 엔티티가 수정되는 시점에 호출된다.

@MappedSuperclass
@Getter
public abstract class BaseEntity {

    @Column(updatable = false)
    private LocalDateTime createdDate; // 등록일
    private LocalDateTime updatedDate; // 수정일
    
    @PrePersist
    public void prePersist() {
        LocalDateTime now = LocalDateTime.now();
        createdDate = now;
        updatedDate = now;
    }
    
    @PreUpdate
    public void preUpdate() {
        updatedDate = LocalDateTime.now();
    }
}

Base Entity 상속

Base Entity에 @MappedSuperclass 애너테이션을 사용하였다. Base Entity 클래스에 존재하는 등록일, 수정일 필드를 상속받는다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment extends BaseEntity {

    private String content; // 작성 내용
}

Test Code 작성

테스트 코드에서 등록일, 수정일에 관련된 코드를 작성하지 않았음에도 출력을 보면 시간이 저장되어 있음을 확인할 수 있습니다.

@SpringBootTest
@Transactional
class CommentTest {

    @PersistenceContext
    EntityManager em;

    @Test
    void test() {
        Comment comment = Comment.builder()
            .content("test")
            .build();

        em.persist(comment);

        comment.updateContent("newContent");

        em.flush();
        em.clear();

        Comment findComment = em.find(Comment.class, comment.getId());
        System.out.println("findComment.getCreatedDate() = " + findComment.getCreatedDate());
        System.out.println("findComment.getUpdatedDate() = " + findComment.getUpdatedDate());
    }
}

출력
findComment.getCreatedDate() = 2024-02-29T00:09:08.748283
findComment.getUpdatedDate() = 2024-02-29T00:09:08.769281

2. Spring Data Jpa의 Auditing

Spring Data JPA 또한 Auditing 기능을 제공합니다. 순수한 JPA와 큰 차이는 없는 것 같습니다.

Base Entity 생성

순수한 JPA에서 했듯이 Base Entity를 생성합니다. 각 애너테이션의 역할을 다음과 같습니다.

  • @EntityListeners(AuditingEntityListener.class) : 해당 클래스에 Auditing 기능을 포함한다
  • @CreatedDate : 엔티티가 생성되어 저장될 때 시간을 자동으로 저장한다
  • @LastModifiedDate : 조회한 엔티티의 값을 변경할 때 시간을 자동으로 저장한다
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate; // 등록일
    
    @LastModifiedDate
    private LocalDateTime updatedDate; // 수정일
}

메인 클래스

스프링부트 메인 클래스에 @EnableJpaAuditing 애너테이션을 추가합니다.

@SpringBootApplication
@EnableJpaAuditing
public class DemoApplication {

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

Base Entity 상속

순수한 JPA에서 설명한 내용과 동일합니다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment extends BaseEntity {

    private String content; // 작성 내용
}

Test Code 작성

순수한 JPA에서 설명한 내용과 동일합니다.

@SpringBootTest
@Transactional
class CommentTest {

    @PersistenceContext
    EntityManager em;

    @Test
    void test() {
        Comment comment = Comment.builder()
            .content("test")
            .build();

        em.persist(comment);

        comment.updateContent("newContent");

        em.flush();
        em.clear();

        Comment findComment = em.find(Comment.class, comment.getId());
        System.out.println("findComment.getCreatedDate() = " + findComment.getCreatedDate());
        System.out.println("findComment.getUpdatedDate() = " + findComment.getUpdatedDate());
    }
}

출력
findComment.getCreatedDate() = 2024-02-29T00:09:08.748283
findComment.getUpdatedDate() = 2024-02-29T00:09:08.769281
Total
Today
Yesterday
최근에 올라온 글
«   2024/11   »
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