본문 바로가기
Back-End/JPA

JPA) 벌크 연산

by 어렵다어려웡 2022. 3. 30.

Entity 를 수정하려면 영속성 컨텍스트변경 감지 기능이나 병합을 사용하고,

삭제하려면 EntityManager.remove() 를 사용한다.

 

하지만 이 방법으로 수 백개 이상의 Entity 를 하나씩 처리하기에는 시간이 너무 오래걸린다.

이럴 떄 여러 건을 한번에 수정하거나 삭제하는 벌크 연산을 쓰면 된다.

 

String qlString = 
		"update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount";

int resultCount = em.createQuery(qlString)
            .setParamter("stockAmoun", 10)
            .executeUpdate();

 

벌크 연산executeUpdate() 메서드를 사용한다.

이 메서드는 벌크 연산으로 영향받은 Entity 건수를 반환한다.

 

삭제도 같은 메서드를 사용한다

String qlString=
	"delete from Product p where p.price < :price";

int resultCount = em.createQuery(qlString)
        .setParameter("price", 100);
        .executeUpdate();

벌크 연산의 주의점

벌크 연산 을 사용할 때는 벌크연산이 영속성 컨텍스트를 무시하고 DB에 직접 쿼리한다는 점을 주의해야 한다.

벌크 연산시 어떤 문제가 생기는지 알아보자.

 

DB에는 가격이 1000원인 상품이 있다.

 

Product productA = 
	em.createQuery("select p from Product p where p.name = :name",
			Product.class)
			.setParameter("name", "productA")
			.getSingleResult();

System.out.println(productA.getPrice());

//벌크연산으로 모든 상품 가격상승
em.createQuery("update Product p set p.price = p.price * 1.1")
		.executeUpdate();
        
System.out.println(productA.getPrice());

 

1) 가격이 1000원인 상품을 조회한다. 상품은 영속성 컨텍스트에서 관리된다.

2) 벌크 연산으로 모든 상품의 가격이 10% 상승했다 따라서 상품A의 가격은 1100원이 되어야 한다.

3) 벌크 연산을 수행한 후에 상품A의 가격을 출력하면 1000원이 나온다.

 

벌크 연산은 영속성 컨텍스트 를 통하지 않고 DB에 직접 쿼리한다.

따라서 영속성 컨텍스트에 있는 상품과 DB의 상품 가격이 다를 수 있다.

 

그래서 벌크 연산을 주의해서 사용해야 한다.

이런 문제를 해결하는데 다양한 방법이 존재한다.

  1. em.refresh() 사용

벌크 연산을 수행한 직후에 정확한 상품A Entity를 사용해야 한다면

em.refresh() 를 사용해서 DB에 상품A를 다시 조회하면 된다.

→ em.refresh(productA);

 

 2. 벌크 연산을 먼저 실행

가장 실용적인 해결책은 가장 먼저 연산하는 것이다.

예를 들어 가장 먼저 벌크연산을 진행하고 상품A를 조회하면

벌크연산으로 인해 변경된 상품A를 조회하게 된다.

 

 3. 벌크 연산 수행 후 영속성 컨텍스트 초기화

벌크 연산을 수행한 직후 영속성 컨텍스트를 초기화해서 영속성 컨텍스트에 있는 Entity 를 제거하는 방법도 좋은 방법이다. 그렇지 않으면 Entity 를 조회할때 영속성 컨텍스트에 남아있는 Entity 를조회할 수 있는데

이 Entity 에는 벌크연산이 적용되지 않아있다.

 

영속성 컨텍스트를 초기화하면 이후 Entity 를 조회할 떄 벌크 연산이 적용된 DB에서 Entity 를 조회한다.

 

벌크 연산영속성 컨텍스트2차 캐시를 무시하고 DB에 직접 실행한다.

따라서 영속성 컨텍스트와 DB 간에 데이터 차이가 발생할 수 있으므로 주의해서 써야한다.

 

가능하다면 벌크 연산을 가장 먼저 수행하는 것이 좋고 상황에 따라서 영속성 컨텍스트를 초기화 하는 것도 필요하다.

 

Spring Data JPA 에서 사용

Spring Data JPA에서 벌크 연산을 적용시켜 사용하려면 @Modifying 어노테이션을 쓰면 된다.

public interface XXXRepository extends JpaRepository<XXX, Long> {

@Modifying
    @Query(query)
    메서드

}