JPA Auditing

JPA Auditing์ด๋ž€?

Java์—์„œ ORM ๊ธฐ์ˆ ์ธ JPA๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„๋ฉ”์ธ์„ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์— ๋งคํ•‘ํ•  ๋•Œ ๊ณตํ†ต์ ์œผ๋กœ ๋„๋ฉ”์ธ๋“ค์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ•„๋“œ๋‚˜ ์ปฌ๋Ÿผ๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ์ƒ์„ฑ์ผ์ž, ์ˆ˜์ •์ผ์ž, ์‹๋ณ„์ž ๊ฐ™์€ ํ•„๋“œ ๋ฐ ์ปฌ๋Ÿผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋„๋ฉ”์ธ๋งˆ๋‹ค ๊ณตํ†ต์œผ๋กœ ์กด์žฌํ•œ๋‹ค๋Š” ์˜๋ฏธ๋Š” ๊ฒฐ๊ตญ ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต๋œ๋‹ค๋Š” ๋ง๊ณผ ์ผ๋งฅ์ƒํ†ตํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ˆ„๊ฐ€, ์–ธ์ œํ•˜์˜€๋Š”์ง€ ๊ธฐ๋ก์„ ์ž˜ ๋‚จ๊ฒจ๋†“์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑ์ผ, ์ˆ˜์ •์ผ ์ปฌ๋Ÿผ์€ ๋Œ€๋‹จํžˆ ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ JPA์—์„œ๋Š” Audit์ด๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Audit์€ ๊ฐ์‹œํ•˜๋‹ค, ๊ฐ์‚ฌํ•˜๋‹ค๋ผ๋Š” ๋œป์œผ๋กœ Spring Data JPA์—์„œ ์‹œ๊ฐ„์— ๋Œ€ํ•ด์„œ ์ž๋™์œผ๋กœ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ์„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์กฐํšŒ๋ฅผ ์ˆ˜ํ–‰ํ•œ ํ›„์— update๋ฅผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋งค๋ฒˆ ์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, audit์„ ์ด์šฉํ•˜๋ฉด ์ž๋™์œผ๋กœ ์‹œ๊ฐ„์„ ๋งคํ•‘ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ”์— ๋„ฃ์–ด์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

1. Auditi ์‚ฌ์šฉ ์˜ˆ์ œ ์ฝ”๋“œ

build.grade์— ์˜์กด์„ฑ ์ถ”๊ฐ€

dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.projectlombok:lombok')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
}

๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ gradle๋กœ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ spring-boot-starter-data-jpa๋งŒ ์ถ”๊ฐ€ํ•ด๋„ Audit์„ ํ•˜๋Š”๋ฐ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์ž๋ฐ” 1.8 ์ด์ƒ๋ถ€ํ„ฐ๋Š” ๊ธฐ์กด์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ๋˜ Date, Calander ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  LocalDate, LocalDateTime ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ LocalDateTime ๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ” ์‚ฌ์ด์˜ ๋งคํ•‘์ด ์•ˆ๋˜๋˜ ์ด์Šˆ๋Š” ํ•˜์ด๋ฒ„๋„ค์ดํŠธ 5.2 ๋ฒ„์ „๋ถ€ํ„ฐ ํ•ด๊ฒฐ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

BaseTimeEntity.java

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

    // Entity๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์ €์žฅ๋  ๋•Œ ์‹œ๊ฐ„์ด ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
    @CreatedDate
    private LocalDateTime createdDate;

    // ์กฐํšŒํ•œ Entity ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ๋•Œ ์‹œ๊ฐ„์ด ์ž๋™ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
    @LastModifiedDate
    private LocalDateTime modifiedDate;

}
์–ด๋…ธํ…Œ์ด์…˜
์„ค๋ช…

@MappedSuperclass

JPA Entity ํด๋ž˜์Šค๋“ค์ด ํ•ด๋‹น ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•  ๊ฒฝ์šฐ createDate, modifiedDate๋ฅผ ์ปฌ๋Ÿผ์œผ๋กœ ์ธ์‹

@EntityListeners(AuditingEntityListener.class

ํ•ด๋‹น ํด๋ž˜์Šค์— Auditing ๊ธฐ๋Šฅ์„ ํฌํ•จ

@CreatedDate

Entity๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์ €์žฅ๋  ๋•Œ ์‹œ๊ฐ„์ด ์ž๋™ ์ €์žฅ

@LastModifiedDate

์กฐํšŒํ•œ Entity์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ๋•Œ ์‹œ๊ฐ„์ด ์ž๋™ ์ €์žฅ

ํด๋ž˜์Šค ์ƒ์†

Posts.java

@Getter
@NoArgsConstructor
@Entity
public class Posts 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 Posts(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;
    }
}

JPA Auditing ํ™œ์„ฑํ™”

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

Posts ํด๋ž˜์Šค๊ฐ€ @MappedSuperclass๊ฐ€ ์ ์šฉ๋œ BaseTimeEntity ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•˜๊ธฐ ๋•Œ๋ฌธ์— JPA๊ฐ€ ์ƒ์„ฑ์ผ์ž, ์ˆ˜์ •์ผ์ž ์ปฌ๋Ÿผ์„ ์ธ์‹ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ ํ›„ BaseTimeEntity ํด๋ž˜์Šค์˜ Auditing ๊ธฐ๋Šฅ์œผ๋กœ ์ธํ•ด์„œ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‹œ์ ์— ํ”Œ๋Ÿฌ์‹œ๊ฐ€ ํ˜ธ์ถœํ•  ๋•Œ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์ž๋™์œผ๋กœ ์‹œ๊ฐ„ ๊ฐ’์„ ์ฑ„์›Œ์ฃผ๋Š”๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ์˜ Entry ํฌ์ธํŠธ์ธ ์‹คํ–‰ ํด๋ž˜์Šค์— @EnableJpaAuditing ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•˜์—ฌ JPA Auditing์„ ํ™œ์„ฑํ™” ํ•ด์•ผํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋ง์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰

    @Test
    public void BaseTimeEntity_๋“ฑ๋ก() throws Exception{
        //given
        LocalDateTime now = LocalDateTime.of(2019,6,4,0,0,0);

        postsRepository.save(Posts.builder()
                .title("title")
                .content("content")
                .author("author")
                .build());

        //when
        List<Posts> postsList = postsRepository.findAll();
        Posts posts = postsList.get(0);

        System.out.println(">>createdDate="+ posts.getCreatedDate() + ", modifiedDate=" + posts.getModifiedDate());
        
        // then
        assertThat(posts.getCreatedDate()).isAfter(now);
        assertThat(posts.getModifiedDate()).isAfter(now);
    }

๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์‹ค์ œ๋กœ Auditing ๊ธฐ๋Šฅ์ด ํ™œ์„ฑํ™” ๋˜์–ด ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์ƒ์„ฑ์ผ์ž, ์ˆ˜์ •์ผ์ž ๊ฐ’์„ ์ž๋™์œผ๋กœ ์ฑ„์›Œ์ฃผ๋Š”์ง€ ํ™•์ธํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

์‹คํ–‰๊ฒฐ๊ณผ

์ฐธ์กฐ : https://velog.io/@sa1341/JPA-Auditing

Last updated