[ TIL ] JPA 벌크 연산 사용시 주의점.

charco·2021년 10월 17일
0

나도TIL

목록 보기
48/55

벌크연산이란?

먼저 엔티티를 수정하려면 트랜잭션 안에서 엔티티를 수정하여
영속성 컨텍스트가 변경 감지를 하게 하면 된다.

// 트랜잭션 시작

// 수정 작업
member.setName("바뀐 이름");

// 커밋 -> 커밋 시에 영속성 컨텍스트가 변경감지를 시행하고 수정 쿼리를 DB에 날린다.

삭제의 경우에도 트랜잭션 안에서 EntityManager 의 remove() 메서드를 호출하면 된다.

하지만 이 방법으로 수백개 이상의 엔티티를 하나씩 처리하기에는 시간이 너무 오래 걸린다.
이럴때 여러건을 한번에 처리하는 벌크연산을 사용하면 된다.

예제

String queryString = 
	"update Member m from set m.name = '아저씨' where m.age > :age";
    
int resultCount = em.createQuery(queryString)
			.setParameter("age", 40)
            		.executeUpdate(); // 벌크 연산 사용.

위의 예제는 나이가 40 이상인 로우들의 name 을 아저씨로 바꾼다.


벌크 연산의 주의점

벌크 연산이 사용되면 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리한다.
이럴때 DB에 반영된 변경이 영속성컨텍스트에는 반영되지 않아
데이터 무결성이 깨지는 문제가 발생할 수 있다.

// 이 엔티티의 age 는 45이다.
Member member = em.createQuery("select m from MEmber m where m.name =:name", Member.class)
				.setParameter("name", "아저씨10")
               			.getSingleResult();

// 영속성 컨텍스트를 거치지 않고 바로 DB로 쿼리를 날린다.
em.createQuery("select Member m set m.age = 12")
	.executeUpdate();
    
// 나이가 바뀌지 않았다...
System.out.println("member.getAge() = " + member.getAge()); // 45 

위의 슬픈 예제 코드가 진행되는 과정을 보자.

  1. name 이 아저씨10 인 Member를 조회한다. 조회에 성공했으니 이 엔티티는 현재 영속성 컨텍스트에 있다.
  2. 모든 Member 의 나이를 12로 바꾸는 벌크연산을 시행한다. 영속성 컨텍스트를 거치지 않고 바로 DB로 쿼리가 날아간다. 따라서 DB에만 변경이 적용됐다.
  3. 엔티티의 age 를 조회한다. 영속성 컨텍스트에는 아무 변화가 없었기 때문에 45 가 그대로 출력된다.

해결책

  1. em.refresh() 사용
    위의 문제를 해결하려면 벌크 연산 직후에 em.refresh(member)를 사용해 아저씨10 을 다시 조회하면 된다.

  2. 벌크 연산 먼저 실행
    간단하다. Update 벌크 연산을 먼저 실행하고 조회하면 된다.

  3. 벌크 연산 수행 후 영속성 컨텍스트 초기화
    벌크 연산을 수행한 직후에 영속성 컨텍스트에 남아있는 엔티티를 제거한다.
    영속성 컨텍스트에 해당 엔티티가 존재하지 않을 것이기 때문에 DB에 조회 쿼리가 날아갈 것이다.

profile
아직 배우는 중입니다

0개의 댓글