JPA 사용시 복잡한 쿼리를 작성할때 JPQL 또는 QueryDSL 을 사용하게 된다.
JPQL 또는 QueryDSL 의 공통점은 1차 캐시에 저장되어있는 객체를 조회 하는것이 아닌 다이렉트로 DB에서 데이터를 조회하게 된다. 이 경우에서 일어날 수 있는 문제를 짚고 넘어가고자 한다.
public void updateMember(){
jpaQueryFactory.update(memberMst)
.set(memberMst.username,"Changed")
.where(memberMst.id.goe(3))
.execute();
}
위 코드를 실행해보도록 하자.
@Transactional
public void updateMember(){
memberQueryRepository.updateMember();
log.info("실행완료");
}
위 코드 실행 즉시 아래와 같이 SQL문을 flush 한다.
영속성 컨텍스트에 이미 존재하는 객체와 DB에서 조회해온 객체 아이디가 같을 경우 1차 캐시에 존재하는 데이터를 사용한다.
발생할 수있는 문제를 살펴보자
-[STEP1] : 디비에 있는 전체 맴버를 조회 해온다.
현재 디비에 들어가있는 Member Data 는 아래와 같다.
-[STEP2] : 디비에 있는 전체 username 을 Changed 로 바꾼다.
-[STEP3] : 디비에 있는 데이터를 다시 조회 한후 변화된 데이터를 살펴본다.
@Transactional
public void updateMember(){
//[STEP1] : 디비에 있는 멤버를 전체 조회 해온다.
List<MemberMst> members = memberQueryRepository.findAllMembers();
//[STEP2] : 디비에 있는 전체 멤버의 이름을 Changed 로 바꾼다.
memberQueryRepository.updateMember();
log.info("실행완료");
//[STEP3] : 디비에서 직접 조회 한 데이터를 살펴본다.
List<MemberMst> membersAfterUpdate = memberQueryRepository.findAllMembers();
membersAfterUpdate.forEach(System.out::println);
}
---------------------------------------------------------------------------
// [STEP1] : 멤버 전체 조회
public List<MemberMst> findAllMembers(){
return jpaQueryFactory.selectFrom(memberMst).fetch();
}
//[STEP2]
public void updateMember(){
jpaQueryFactory.update(memberMst)
.set(memberMst.username,"Changed")
.execute();
}
위 모든 코드가 종료 되었을때 어떤 데이터가 찍히는지 살펴보자.
😕 데이터를 살펴보니 update 문이 전혀 반영이 되지 않았다.
코드를 살펴보자.
@Transactional
public void updateMember(){
List<MemberMst> members = memberQueryRepository.findAllMembers();
memberQueryRepository.updateMember();
log.info("실행완료");
List<MemberMst> membersAfterUpdate = memberQueryRepository.findAllMembers();
membersAfterUpdate.forEach(System.out::println);
membersAfterUpdate.get(0).changeAge(55);
}
public void updateMember(){
jpaQueryFactory.update(memberMst)
.set(memberMst.username,"Changed")
.execute();
}
나머지 멤버의 이름이 정상적으로 변경되었지만 1번 맴버는 나이가 변경되었고 이름은 변경되지 않았다.
이는 첫번째 멤버의 나이를 변경할때 영속성컨텍스트에 있는 첫번째 멤버를 기준으로 하여 변경감지가 일어났기 때문에다.
다시 테스트 해보자
public void updateMember(){
jpaQueryFactory.update(memberMst)
.set(memberMst.username,"Changed")
.execute();
entityManager.flush();
entityManager.clear();
}
연산후 1차 테스트와 다른 결과가 나오는지 확인해보자.
이제는 첫번째 맴버인 6번까지 username 이 정상적으로 바뀌는것을 볼 수 있다.