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

charco·2021년 10월 17일
0

나도TIL

목록 보기
48/55
post-custom-banner

벌크연산이란?

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

// 트랜잭션 시작

// 수정 작업
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
아직 배우는 중입니다
post-custom-banner

0개의 댓글