DB/JPA

@PrePersist...

코딩딩코 2023. 1. 14.

Entity를 DB에 적용하기 전, 후로 커스텀 콜백을 요청하는 어노테이션이 있습니다.

@PrePersist
@PostPersist
@PostLoad
@PreUpdate
@PostUpdate
@PreRemove
@PostRemove

어노테이션 이름으로 유추해 보아도 무슨 역할을 하는지 알 수 있습니다.

하나씩 살펴보겠습니다.

 

@PrePersist & @PostPersist

@Slf4j
@Entity
@Builder
@Getter
@Table(name = "postMst")
public class Post extends BaseTimeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long postCode;

    @NotNull
    @Column(name = "postContent")
    private String content;

    @PrePersist
    private void logInfo() {
        log.info("INSERT 전에 호출되었습니다.");
    }
    
    @PostPersist
    private void logPostCode() {
        log.info("postCode: {}", postCode);
    }
}

@PrePersist는 DB에 INSERT가 되기 전에 호출되고 @PostPersist는 INSERT 후에 호출이 됩니다.

@Test
void savePostTest() {
    // given
    Post post = Post.builder()
            .content("게시글5")
            .build();

    // when
    postService.savePost(post);
    // then

}

@Transactional
public void savePost(Post post) {
    entityManager.persist(post);
}



2023-01-14 21:23:49.320  INFO 16584 --- [           main] com.project.instagram.domain.test.Post   : INSERT 전에 호출되었습니다.
Hibernate: 
    /* insert com.project.instagram.domain.test.Post
        */ insert 
        into
            post_mst
            (create_date, update_date, post_content) 
        values
            (?, ?, ?)
2023-01-14 21:23:49.407  INFO 16584 --- [           main] com.project.instagram.domain.test.Post   : postCode: 5

테스트코드를 작성하고 실행해보았습니다.

로그를 보면 INSERT가 일어나기 전에 로그가 하나 찍히고 INSERT가 일어난 후에 로그가 하나 찍혔습니다.

Post 객체의 postCode는 AutoIncreament 된 값으로 확인할 수 있습니다.

 

@PostLoad

@Slf4j
@Entity
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "postMst")
public class Post extends BaseTimeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long postCode;

    @NotNull
    @Column(name = "postContent")
    private String content;

    @PostLoad
    private void logInfo() {
        log.info("SELECT 이후에 호출되었습니다.");
    }
}

DB에서 SELECT 후에 호출하기 위해 사용하는 어노테이션입니다.

Hibernate: 
    select
        post0_.post_code as post_cod1_10_0_,
        post0_.create_date as create_d2_10_0_,
        post0_.update_date as update_d3_10_0_,
        post0_.post_content as post_con4_10_0_ 
    from
        post_mst post0_ 
    where
        post0_.post_code=?
2023-01-14 21:33:19.176  INFO 1752 --- [           main] com.project.instagram.domain.test.Post   : SELECT 이후에 호출되었습니다.

역시 로그를 보면 SELECT 이후에 호출된 것을 확인할 수 있습니다.

@PreUpdate & @PostUpdate

@Slf4j
@Entity
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "postMst")
public class Post extends BaseTimeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long postCode;

    @NotNull
    @Column(name = "postContent")
    private String content;

    @PreUpdate
    private void logDefaultContent() {
        log.info("content: {}", content);
    }

    @PostUpdate
    private void logNewContent() {
        log.info("content: {}", content);
    }
}

 

@PreUpdate: DB에서 UPDATE가 일어나기 전에 호출되게끔 하는 어노테이션

@PostUpdate: DB에서 UPDATE가 일어난 후에 호출되게끔 하는 어노테이션

@Transactional
public void updatePost(Long postCode, String content) {
    Post post = entityManager.find(Post.class, postCode);

    post.setContent(content);
}


2023-01-14 21:39:06.744  INFO 4424 --- [           main] com.project.instagram.domain.test.Post   : content: 업데이트한 게시글입니다.
Hibernate: 
    /* update
        com.project.instagram.domain.test.Post */ update
            post_mst 
        set
            create_date=?,
            update_date=?,
            post_content=? 
        where
            post_code=?
2023-01-14 21:39:06.853  INFO 4424 --- [           main] com.project.instagram.domain.test.Post   : content: 업데이트한 게시글입니다.

 

여기서 우리가 주의해야 할 것이 있습니다.

로그를 보면 UPDATE가 일어나기 전, 후로 로그는 잘 나왔습니다.

하지만 제가 의도한 것은 수정되기 전의 content를 @PreUpdate로 로그를 찍어보는 것과

수정된 후의 content를 @PostUpdate로 로그를 찍어보는 것입니다.

하지만 결과는 둘 다 수정된 후의 content 내용이 찍혔습니다.

 

이유는 더티체킹으로 UPDATE가 일어나서 그런 것입니다.

더티체킹은 트랜잭션 안에서 커밋이 되고 난 후에 일어납니다.

지금 @Transactional 어노테이션을 달아놓았기 때문에 메서드 호출 마지막에 커밋이 일어나서

이미 Post 객체의 content는 setter로 값을 바꾼 뒤이기 때문에 의도한 대로 작동하지 않았습니다.

@PreRemove & @PostRemove

@Slf4j
@Entity
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "postMst")
public class Post extends BaseTimeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long postCode;

    @NotNull
    @Column(name = "postContent")
    private String content;

    @PreRemove
    private void logInfo() {
        log.info("DELETE 전");
    }

    @PostRemove
    private void logInfo2() {
        log.info("DELETE 후");
    }
}

@PreRemove: DB에서 DELETE가 일어나기 전에 호출되게끔 하는 어노테이션

@PostRemove: DB에서 DELETE가 일어난 후에 호출되게끔 하는 어노테이션

 

2023-01-14 21:49:19.896  INFO 18612 --- [           main] com.project.instagram.domain.test.Post   : DELETE 전
Hibernate: 
    /* delete com.project.instagram.domain.test.Post */ delete 
        from
            post_mst 
        where
            post_code=?
2023-01-14 21:49:19.934  INFO 18612 --- [           main] com.project.instagram.domain.test.Post   : DELETE 후

 

'DB > JPA' 카테고리의 다른 글

JPA 즉시 로딩, 지연 로딩  (0) 2023.01.20
JPA Auditing  (0) 2023.01.07
JPA @Entity  (0) 2023.01.06
JPA DTO Mapping  (0) 2022.12.29
JPA 더티 체킹(Dirty Checking)  (0) 2022.12.25

댓글