먼저 엔티티를 수정하려면 트랜잭션 안에서 엔티티를 수정하여
영속성 컨텍스트가 변경 감지를 하게 하면 된다.
// 트랜잭션 시작
// 수정 작업
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
위의 슬픈 예제 코드가 진행되는 과정을 보자.
em.refresh() 사용
위의 문제를 해결하려면 벌크 연산 직후에 em.refresh(member)를 사용해 아저씨10 을 다시 조회하면 된다.
벌크 연산 먼저 실행
간단하다. Update 벌크 연산을 먼저 실행하고 조회하면 된다.
벌크 연산 수행 후 영속성 컨텍스트 초기화
벌크 연산을 수행한 직후에 영속성 컨텍스트에 남아있는 엔티티를 제거한다.
영속성 컨텍스트에 해당 엔티티가 존재하지 않을 것이기 때문에 DB에 조회 쿼리가 날아갈 것이다.