해결된 질문
작성
·
1.2K
2
@Test
public void bulkDelete() {
List<Member> result = queryFactory
.selectFrom(member)
.fetch(); // 104명
for (Member member1 : result) {
System.out.println("member1 = " + member1);
}
long count = queryFactory
.delete(member)
.where(member.age.gt(18))
.execute();
System.out.println("delete count="+count); // 84
List<Member> result1 = queryFactory
.selectFrom(member)
.fetch(); // 20명
for (Member member1 : result1) {
System.out.println("member1 = " + member1);
}
}
답변 3
0
package study.querydsl;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;
import study.querydsl.entity.Member;
import study.querydsl.entity.QMember;
import study.querydsl.entity.Team;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import static study.querydsl.entity.QMember.member;
@Transactional
@SpringBootTest
public class QuerydslBasicTest {
@PersistenceContext
EntityManager em;
JPAQueryFactory queryFactory;
@BeforeEach
public void before() {
queryFactory = new JPAQueryFactory(em);
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
em.persist(teamA);
em.persist(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamA);
Member member4 = new Member("member4", 40, teamA);
em.persist(member1);
em.persist(member2);
em.persist(member3);
em.persist(member4);
em.flush();
System.out.println("영속성 컨텍스트 초기화");
System.out.println("여기서 안하면 before에서 이미 영속성 컨텍스트 안에 team과 member들이 들어가고 시작");
em.clear();
}
@Test
public void bulkUpdate() {
//member1 = 10 -> 비회원
//member2 = 20 -> 비회원
//member3 = 30 -> 유지
//member4 = 40 -> 유지
System.out.println("여기서 영속성 컨텍스트 1차 캐시에 들어가게 됨.");
List<Member> result1 = queryFactory.selectFrom(member).fetch();
for (Member member1 :
result1) {
System.out.println("member1 = " + member1);
}
long count = queryFactory.update(member)
.set(member.username, "비회원")
.where(member.age.lt(28))
.execute();
//em.flush
//em.clear
System.out.println("영속성 컨텍스트 초기화X");
System.out.println("JPQL은 데이터베이스에서 먼저 조회하고 영속성 컨텍스트에 해당 엔티티가 존재하는지 확인");
System.out.println("id로 따지면 1차캐시에 모두 존재하기 때문에 영속성 컨텍스트 1차 캐시에 있는 엔티티 그대로 가져온다.");
List<Member> result = queryFactory.selectFrom(member).fetch();
for (Member member1 :
result) {
System.out.println("member1 = " + member1);
}
}
@Test
public void bulkDelete() {
//member1 = 10 -> 비회원
//member2 = 20 -> 비회원
//member3 = 30 -> 유지
//member4 = 40 -> 유지
List<Member> result = queryFactory.selectFrom(member).fetch();
for (Member member1 :
result) {
System.out.println("member1 = " + member1);
}
long updated = queryFactory.update(member)
.set(member.username, "비회원")
.where(member.age.lt(28))
.execute();
long count = queryFactory.delete(member)
.where(member.age.lt(18))
.execute();
//em.flush
//em.clear
System.out.println("delete count="+count);
System.out.println("영속성 컨텍스트 초기화X");
System.out.println("JPQL은 데이터베이스에서 먼저 조회하고 영속성 컨텍스트에 해당 엔티티가 존재하는지 확인");
System.out.println("데이터베이스에서 이미 삭제된 엔티티는 영속성 컨텍스트 1차 캐시에서 조회해오지 않습니다.");
System.out.println("결론적으로 영속성 컨텍스트는 그대로 남아있고 벌크 연산 결과는 반영되지 않았습니다.");
System.out.println("그 증거로 bulk update 결과는 반영되지 않습니다.");
List<Member> result1 = queryFactory.selectFrom(member).fetch();
for (Member member1 :
result1) {
System.out.println("member1 = " + member1);
}
}
}
0
죄송합니다. Chaerin Yu님께서는 bulkUpdate 후에 영속성 컨텍스트 초기화를 하지 않고 select 쿼리 후 조회했을 때는 바뀐게 반영되지 않는데 bulkDelete에서는 바뀐게 반영된다. 이게 왜 그런지 모르겠다고 질문해주셨습니다.
해당 답변에 안일하게 답변을 달았다가 Chaerin Yu님께서 다시 질문을 주셔서 보게 되었습니다.
그래서 직접 테스트 코드를 짜본 결과 제가 이해한 바로는 두 벌크 연산 테스트 결과의 차이는 JPQL이 먼저 DB에 조회하고 그걸 바탕으로 영속성 컨텍스트의 1차 캐시에 해당 엔티티가 있는지 조회한 후 해당 엔티티가 존재한다면 그걸 그대로 반환하는 데서 오는 것이라고 봤습니다.
bulkUpdate 후에는 해당 엔티티들의 개수와 id는 변하지 않았기 때문에 DB에서 조회한 id 값들도 같을 것이고 1차 캐시에서 해당 엔티티들도 그대로 있을테니 1차 캐시에 있는 엔티티들을 불러온 것이고, bulkDelete 후에는 DB에서 조회한 id 들에 한해서만 영속성 컨텍스트 1차 캐시에서 값을 조회해오는 것이라 DB에서 삭제된 1차 캐시의 해당 엔티티는 불러오지 않았습니다.
결론을 말하자면 bulkDelete도 벌크 연산 후 영속성 컨텍스트에 바로 반영되지는 않는다고 말씀드릴 수 있을 것 같습니다.
제가 테스트한다고 짜보았던 코드를 밑에 첨부드리겠습니다.
감사합니다.
0
수정 벌크 연산 예제에서 전체 Member 조회 후 bulk update 실행한 다음 flush, clear를 거치지 않고 출력하게 되는 경우 upate 처리되기 전과 동일하게 나오고 있습니다.
flush, clear 처리를 해줘야만 28살 미만의 사용자 이름이 '비회원'으로 업데이트가 됩니다.
bulkUpdate 테스트와 bulkDelete 테스트에서 update와 delete를 제외하고는 동일하게 실행했는데 2번째 반복문에서 조회할 때 update에서는 영속성 컨텍스트에서 데이터를 가져오고 delete에서는 delete 직후에 flush 발생한 후, DB에서 다시 가져오는 거로 보여서 질문드렸습니다.
delete 쿼리를 날리면 초기화(flush, clear) 없이도 이미 DB와 영속성 컨텍스트 데이터가 동일합니다.
@Test
@Commit
public void bulkUpdate() {
List<Member> result1 = queryFactory
.selectFrom(member)
.fetch();
for (Member member1 : result1) {
System.out.println("member1 = " + member1);
}
long count = queryFactory
.update(member)
.set(member.username, "비회원")
.where(member.age.lt(28))
.execute();
// em.flush(); // flush 해줘야만 아래에서 조회 시, 28살 미만 회원들 이름이 '비회원'으로 보임
// em.clear();
List<Member> result = queryFactory
.selectFrom(member)
.fetch();
for (Member member1 : result) {
System.out.println("member1 = " + member1);
}
}
감사합니다.