티스토리 뷰
벌크 연산이란?
벌크 연산은 쿼리 한 번으로 여러 테이블의 로우를 변경하는 것이다. 즉 대량의 데이터를 수정(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 - 웹 애플리케이션 개발 편/인프런”를 학습한 내용을 정리한 것
'Java > JPA' 카테고리의 다른 글
| <JPA> 페이징 처리 (3) | 2024.11.07 |
|---|---|
| <JPA> @Enumerated 사용 시 주의할 점 (0) | 2024.10.31 |
| <JPA> 준영속 엔티티를 수정하는 2가지 방법 (0) | 2024.09.30 |
| <JPA> 임베디드 타입이란? (0) | 2024.09.24 |
| <JPA> 즉시 로딩과 지연 로딩이란? (0) | 2024.09.23 |
