본문 바로가기
JPA/스프링 DATA JPA

Data JPA 11 Auditing

by 킹차니 2022. 1. 8.

Auditing

엔티티를 생성, 변경할 때 변경한 사람과 시간을 추적하고 싶을 때

- 등록일

- 수정일

- 등록자

- 수정자

 

 

먼저 순수 JPA를 사용하여 등록일, 수정일을 추가할 수 있도록 해보자.

먼저 아래와 같이 수정일과 등록일을 위한 엔티티가 필요하다. (@MappedSuperclass 사용 )

@Getter
@MappedSuperclass
public class JpaBaseEntity {

    @Column(updatable = false)//생성일은 변경될 수 없다.
    private LocalDateTime createdDate;
    private LocalDateTime updatedDate;

    @PrePersist//Persist하기 전에 호출
    public void prePersist(){
        LocalDateTime now = LocalDateTime.now();
        createdDate = now;
        updatedDate = now;
    }

    @PreUpdate//Update하기 전에 호출
    public void preUpdate(){
        updatedDate = LocalDateTime.now();
    }
}

등록일과 수정일을 추가하고 싶은 엔티티가 위의 JpaBaseEntity를 extends하면 된다.

public class  Member extends JpaBaseEntity{ /* extends JpaBaseEntity */

    @Id @GeneratedValue
    @Column(name="member_id")
    private Long id;
    private String username;
    private int age;

    @ToString.Exclude
    @JoinColumn(name="team_id")
    @ManyToOne(fetch=FetchType.LAZY)
    private Team team;
    /// 중략... ///
}

이제 이를 테스트해보자.

@Test
void JpaEventBaseEntity() throws Exception {
    //given
    Member  member = new Member("member1", 10, null);
    memberRepository.save(member); /* @PrePersist 발동 */

    Thread.sleep(1000);//1초 지연
    member.setUsername("멤버1");

    em.flush(); /* @PreUpdate 발동*/
    em.clear();

    //when
    Member findMember = memberRepository.findById(member.getId()).get();

    //then
    System.out.println("findMember.getCreatedDate() = " + findMember.getCreatedDate());
    System.out.println("findMember.getUpdatedDate() = " + findMember.getUpdatedDate());
}



출력결과:

findMember.getCreatedDate() = 2022-01-08T14:12:04.955749
findMember.getUpdatedDate() = 2022-01-08T14:12:05.982232

DB에도 잘 반영된 것을 볼 수 있다.

 

 

하지만 데이터 JPA는 위와 같은 기능을 더 쉽게 제공한다.

 


데이터 JPA로 Auditing

 

이를 위해서는 먼저 아래의 설정을 메인 클래스에 해줘야 한다.

@EnableJpaAuditing

@EnableJpaAuditing /* 요 어노테이션 잊지 말자! */
@SpringBootApplication
public class DataJpaApplication {
   public static void main(String[] args) {
      SpringApplication.run(DataJpaApplication.class, args);
   }
}

 

 

이제 Base 엔티티를 만들어보자.

@EntityListeners(AuditingEntityListener.class) // 이벤트 기반으로 동작한다!
@Getter
@MappedSuperclass
public class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
}

 

@CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy 와 같은 어노테이션들을 제공한다. 

또한 @EntityListeners 어노테이션도 잊지 말자.

 

그리고 @CreatedBy, @LastModifiedBy와 같은 기능을 사용하기 위해서는 아래와 같이 생성자, 작성자 아이디를 반환해주는 메서드가 필요하다.

@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {
   public static void main(String[] args) {
      SpringApplication.run(DataJpaApplication.class, args);
   }

   @Bean /* @CreatedBy, @LastModifiedBy를 위해 필요하다. */
   public AuditorAware<String> auditorProvider(){
      return () -> Optional.of(UUID.randomUUID().toString());
   }
}

이를 테스트 해보자.

@Test
void JpaEventBaseEntity() throws Exception {
    //given
    Member  member = new Member("member1", 10, null);
    memberRepository.save(member); /* @PrePersist 발동 */

    Thread.sleep(1000);//1초 지연
    member.setUsername("멤버1");

    em.flush(); /* @PreUpdate 발동*/
    em.clear();

    //when
    Member findMember = memberRepository.findById(member.getId()).get();

    //then
    System.out.println("findMember.getCreatedDate() = " + findMember.getCreatedDate());
    System.out.println("findMember.getLastModifiedDate() = " + findMember.getLastModifiedDate());
    System.out.println("findMember.getCreatedBy() = " + findMember.getCreatedBy());
    System.out.println("findMember.getLastModifiedBy() = " + findMember.getLastModifiedBy());
}


실행결과:

findMember.getCreatedDate() = 2022-01-08T14:41:29.694353
findMember.getLastModifiedDate() = 2022-01-08T14:41:30.722636
findMember.getCreatedBy() = 4e31bd4a-08d7-4a73-92d4-09e6958c7254
findMember.getLastModifiedBy() = 7e1fcd67-34bc-4980-9262-ad8fcd83c6cb

 

 


 + 추가

 

그런데 만약 어떤 엔티티에서는 생성일과 수정일은 필요하지만 작성자와 수정자는 불필요할 수 있다. 하여 이러한 상황에서는 아래와 같이 한다.

 

생성일, 수정일 BaseEntity를 만들고, 

@EntityListeners(AuditingEntityListener.class) // 이벤트 기반으로 동작한다!
@Getter
@MappedSuperclass
public class DateBaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}

 

이를 BaseEntity가 상속하여 생성자와 수정자를 추가한다. 

@EntityListeners(AuditingEntityListener.class) // 이벤트 기반으로 동작한다!
@Getter
@MappedSuperclass
public class BaseEntity extends  DateBaseEntity{

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
}

 

 

이제 생성일과 수정일만 필요한 엔티티는 DateBase엔티티를 상속받고, 더불어 생성자와 수정자까지 필요한 엔티티는 BaseEntity를 상속받으면 되는 것이다.

 

 

 

김영한님의 인프런 강의와 PDF를 바탕으로 정리하였습니다.