티스토리 뷰

Java/JPA

<JPA> 벌크 연산시 주의할 점

면목동인간 2024. 10. 3. 02:09

벌크 연산이란?

 벌크 연산은 쿼리 한 번으로 여러 테이블의 로우를 변경하는 것이다. 즉 대량의 데이터를 수정(update, delete문)과 삭제하는 연산이다.

 

벌크 연산시 주의할 점?

 벌크 연산을 사용할 때는 주의할 점이 있는데 그것은 벌크 연산은 영속성 컨텍스트를 무시하고 DB에 바로 쿼리를 실행하는 것이다. 아래 예제를 통해 무엇이 문제인지 알아보았다.

 

문제점 예제

Book.java

@Entity
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String author;

    private String bookName;

    // getter, setter 생략
}

Main.java

// 엔티티 매니저 생력

System.out.println("book 저장");
Book book = new Book();
book.setAuthor("author");
book.setBookName("jpa");

em.persist(book);

System.out.println("book 수정");
int updateCount = em.createQuery("update Book b set b.author = 'update_author'").executeUpdate();
System.out.println("updateCount: " + updateCount);

System.out.println("book.author: " + book.getAuthor());

tx.commit();

실행 결과

book 저장
Hibernate: 
    select
        next value for Book_SEQ
book 수정
Hibernate: 
    /* insert for
        jpabook.jpashop.domain.Book */insert 
    into
        Book (author, bookName, id) 
    values
        (?, ?, ?)
Hibernate: 
    /* update
        Book b 
    set
        b.author = 'update_author' */ update Book 
    set
        author='update_author'
updateCount: 1
book.author: author

 실행 결과를 보면 update문 전에 insert문이 실행 된 것을 볼 수 있다. 이것은 벌크 연산인 update문 전에 flush(영속성 컨텍스트에 저장된 쿼리들을 실행)가 실행되었기 때문이다. 그런데 이상한 점이 있는데 그것은 update를 실행했는데 book.author를 확인해 보니 update 하기 전인 author이 출력되었다. 왜냐하면 DB상에 update를 실행하여도 영속성 컨텍스트 내에 있는 book은 그대로 남아있기 때문이다. 이를 해결하기 위해서는 컨텍스트를 초기화한 후 다시 조회해야 한다.

 

문제점 해결 예제

Main.java

// 엔티티 매니저 생략

System.out.println("book 저장");
Book book = new Book();
book.setAuthor("author");
book.setBookName("jpa");

em.persist(book);

System.out.println("book 수정");
int updateCount = em.createQuery("update Book b set b.author = 'update_author'").executeUpdate();
System.out.println("updateCount: " + updateCount);

// 영속성 컨텍스트 초기화
em.clear();

Book updateBook = em.find(Book.class, book.getId());
System.out.println("updateBook.author: " + updateBook.getAuthor());

tx.commit();

실행 결과

book 저장
Hibernate: 
    select
        next value for Book_SEQ
book 수정
Hibernate: 
    /* insert for
        jpabook.jpashop.domain.Book */insert 
    into
        Book (author, bookName, id) 
    values
        (?, ?, ?)
Hibernate: 
    /* update
        Book b 
    set
        b.author = 'update_author' */ update Book 
    set
        author='update_author'
updateCount: 1
Hibernate: 
    select
        b1_0.id,
        b1_0.author,
        b1_0.bookName 
    from
        Book b1_0 
    where
        b1_0.id=?
updateBook.author: update_author

 실행 결과를 보면 생각한대로 정상적으로 출력하였다. 이는 중간에 컨텍스트를 초기화 후 다시 조회하였기 때문이다. 만약 초기화 하지 않고 새로 조회한다면 이미 기존에 Book 엔티티가 영속성 컨텍스트 1차 캐시 내에 존재하기 때문에 select가 실행되지 않고 기존에 존재한 엔티티를 반환할 것이다. 그러므로 초기화를 실행해야 한다.

 

정리

 벌크 연산은 영속성 컨텍스트를 무시하고 DB에 쿼리를 직접 사용하기 때문에 사용 후 영속성 컨텍스트를 초기화해야 한다. 초기화하지 않을 경우 컨텍스트 내에 엔티티가 존재하기 때문에 컨텍스트에 있는 엔티티와 실제 DB 상에 있는 엔티티가 다르게 된다.

 


본 포스팅은 “실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 편/인프런”를 학습한 내용을 정리한 것

댓글
최근에 올라온 글
«   2026/03   »
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 31
글 보관함
Total
Today
Yesterday