본문 바로가기

JPA

[Spring Data JPA] Auditing

Auditing

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

  • 등록일
  • 수정일
  • 등록자
  • 수정자

순수 JPA 사용하여 등록일, 수정일 적용해보자.

@MappedSuperclass
@Getter
public class JpaBaseEntity {

    @Column(updatable = false)
    private LocalDateTime createTime;
    private LocalDateTime updateTime;

    @PrePersist
    public void prePersist() {
        this.createTime = LocalDateTime.now();
        this.updateTime = LocalDateTime.now();
    }

    @PreUpdate
    public void preUpdate() {
        this.updateTime = LocalDateTime.now();
    }
}
public class Member extends JpaBaseEntity {}

 

확인 코드

@Test
public void JpaEventBaseEntity() throws Exception{
    // given
    Member member = new Member("member1");
    memberRepository.save(member); // @PrePersist

    Thread.sleep(100);
    member.setUsername("member2");

    em.flush(); // @PreUpdate
    em.clear();

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

    // then
    System.out.println("findMember.getCreateTime() = " + findMember.getCreateTime());
    System.out.println("findMember.getUpdateTime() = " + findMember.getUpdateTime());
    
}

JPA 주요 이벤트 어노테이션 

  • @PrePersist, @PostPersist
  • @PreUpdate, @PostUpdate

스프링 데이터 JPA 사용을 사용하여 더욱 깔끔하게 코드를 작성할 수 있다.

 

사용 어노테이션

  • @CreatedDate
  • @LastModifiedDate
  • @CreatedBy
  • @LastModifiedBy

설정

@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {

    public static void main(String[] args) {
       SpringApplication.run(DataJpaApplication.class, args);
    }
}
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {

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

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}
  • @EnableJpaAuditing 스프링 부트 설정 클래스에 적용해야함
  • @EntityListeners(AuditingEntityListener.class) 엔티티에 적용

스프링 데이터 Auditing 적용 - 등록자, 수정자

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {

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

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

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

    @LastModifiedBy
    private String lastModifiedBy;
}
@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {

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

    @Bean
    public AuditorAware<String> auditorProvider() {
       // 실무에서는 SecurityContext에서 사용자를 꺼내는 등의 작업을 하면 됨.
       return () -> Optional.of(UUID.randomUUID().toString());
    }
}
  • 등록자, 수정자를 처리해주는 `AuditorAware` 스프링 빈 등록
  • 실무에서는 세션 정보나, 스프링 시큐리티 로그인 정보에서 ID를 받음
  • 주의: DataJpaApplication @EnableJpaAuditing도 함께 등록해야 한다.

참고

public class BaseTimeEntity {
     @CreatedDate
     @Column(updatable = false)
     private LocalDateTime createdDate;
     @LastModifiedDate
     private LocalDateTime lastModifiedDate;
     
 }
 
public class BaseEntity extends BaseTimeEntity {
    @CreatedBy
    @Column(updatable = false)
    private String createdBy;
    @LastModifiedBy
    private String lastModifiedBy;
}
  • 실무에서 대부분의 엔티티는 등록시간, 수정시간이 필요하지만, 등록자, 수정자는 없을 수도 있다.
  • 그래서 다음과 같이 Base 타입을 분리하고, 원하는 타입을 선택해서 상속한다.

저장 시점에 저장 데이터만 저장

@EnableJpaAuditing(modifyOnCreate = false)
@SpringBootApplication
public class DataJpaApplication {

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

    @Bean
    public AuditorAware<String> auditorProvider() {
       return () -> Optional.of(UUID.randomUUID().toString());
    }
}

저장시점에 등록일, 등록자는 물론이고, 수정일, 수정자도 같은 데이터가 저장된다.

데이터가 중복 저장되는 것 같지만, 이렇게 해두면 변경 컬럼만 확인해도 마지막에 업데이트한 유저를 확인 할 수 있으므로 유지보수 관점 에서 편리하다. 이렇게 하지 않으면 변경 컬럼이 `null` 일때 등록 컬럼을 또 찾아야 한다. 그럼에도 불구하고 저장 시점에 저장 데이터만 저장하고 싶다면 위를 참고하면 된다. - @EnableJpaAuditing(modifyOnCreate = false) 옵션을 사용

 

전체 적용 방법 - (META-INF/orm.xml)

// META-INF/orm.xml
<?xml version="1.0" encoding="UTF-8"?>
 <entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
 http://xmlns.jcp.org/xml/ns/persistence/orm_2_2.xsd"
                  version="2.2">
     <persistence-unit-metadata>
         <persistence-unit-defaults>
             <entity-listeners>
                 <entity-listener
 class="org.springframework.data.jpa.domain.support.AuditingEntityListener"/>
             </entity-listeners>
         </persistence-unit-defaults>
     </persistence-unit-metadata>
   
 </entity-mappings>
  •  @EntityListeners(AuditingEntityListener.class) 를 생략하고 스프링 데이터 JPA 가 제공하는 이벤트를 엔티티 전체에 적용하려면 orm.xml에 다음과 같이 등록하면 된다.