2019. 2. 12. 00:19ㆍWeb/JPA
JPA - 영속성 컨텍스트와 JPQL
쿼리 후 영속 상태인 것과 아닌것
-JPQL의 조회 대상은 엔티티, 임베디드 타입, 값 타입 같이 다양하다. 하지만 엔티티를 조회하면 영속성 컨텍스트에 관리되지만 나머지는 관리되지 않는다. 예를 들어 임베디드 타입만 참조해서 조회했을 경우에 값을 변경해서 플러시 시점에 반영되지 않는다. 하지만 엔티티를 조회하여 엔티티가 가지고 있는 임베디드타입을 변경했을 경우에는 플러시 시점에 변경이 반영이 된다.
JPQL로 조회한 엔티티와 영속성 컨텍스트
여기서 동일하다는 것은 엔티티의 모든 필드값이 동일하다는 것을 의미하는 것이 아닌 엔티티의 식별자값이 동일함을 의미한다. 즉, 만약 1차 캐시의 member1의 이름이 "여성게"이고 , 디비에 있는 member1의 이름이 "소라게"여도 식별자 값이 같음으로 "여성게"로 조회가 되게 된다. (물론 이런 상황 자체가 있는 것이 잘못된 것이긴함) 하지만 만약 영속성컨텍스트에 디비에서 조회한 엔티티가 존재하지 않으면 디비에서 가져온 엔티티를 영속성 컨텍스트에 넣는다. 왜 그런가?
1. 새로운 엔티티를 영속성 컨텍스트에 하나 더 추가한다. -> 식별자값은 유일함으로 이것은 말이안됨.
2.기존 엔티티를 새로 검색한 엔티티로 대체한다. -> 그럴싸 하지만 만약 엔티티가 수정중에 있다면 아주 큰문제가됨.
3.기존 엔티티는 그대로 두고 새로 검색한 엔티티를 버린다. -> 이 규칙을 따르게 되는 것이다. 영속성 컨텍스트는 영속 상태인 엔티티의 동일성을 보장해야하기 때문
find() vs JPQL
JPQL과 플러시 모드
쿼리와 플러시모드
JPQL은 영속성 컨텍스트에 있는 데이터를 고려하지 않고 디비에서 데이터를 조회한다. 따라서 JPQL을 실행하기 전에 영속성 컨텍스트의 내용을 디비에 반영해야한다. 그렇지 않으면 의도하지 않은 결과가 발생할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public void batchQuery() { JPAQuery query = new JPAQuery(em); QMemberJPQL qMember = QMemberJPQL.memberJPQL; tx.begin(); JPAUpdateClause updateBatch = new JPAUpdateClause(em, qMember); long count = updateBatch.where(qMember.username.eq("여성게1")) .set(qMember.username,"바보멍청이").execute(); tx.commit(); if(count == 1) { MemberJPQL member = query.from(qMember) .where(qMember.username.eq("바보멍청이")).uniqueResult(qMember); System.out.println("=====================batchQuery======================"); if(!ObjectUtils.isEmpty(member)) { System.out.println(member.toString()); } System.out.println("=====================batchQuery======================"); } } | cs |
이 예제는 QueryDSL의 수정 배치 쿼리이다. 이 예제의 결과가 어떻게 될것인가? 결과는 정말 의도치 않게 나오게 된다. QueryDSL의 배치쿼리는 영속성컨텍스트와 관계없이 직접 디비로 요청을 보내게 된다. 이러는 순간 디비와 영속성컨텍스트의 동기화는 깨진것이다. 변경을 완료했고 나는 변경된 이름으로 쿼리를 날렸는데, 이게 뭐람 변경전의 "여성게1"이란 결과가 나오게 된다. 이유는 위에서 설명한 것과 같이 영속성컨텍스트에 이미 존재하기 때문에 디비에서 가져온 엔티티가 데이터가 달라도 식별자 값이 같기 때문에 버려지게 되고 수정전에 이름이 출력되게 되는 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public void batchQuery2() { JPAQuery query = new JPAQuery(em); QMemberJPQL qMember = QMemberJPQL.memberJPQL; tx.begin(); JPAUpdateClause updateBatch = new JPAUpdateClause(em, qMember); long count = updateBatch.where(qMember.username.eq("여성게1")) .set(qMember.username,"바보멍청이").execute(); MemberJPQL member2 = query.from(qMember) .where(qMember.username.eq("바보멍청이")).uniqueResult(qMember); member2.setUsername("바보멍청이"); tx.commit(); if(count == 1) { MemberJPQL member = new JPAQuery(em).from(qMember) .where(qMember.username.eq("바보멍청이")).uniqueResult(qMember); System.out.println("=====================batchQuery======================"); if(!ObjectUtils.isEmpty(member)) { System.out.println(member.toString()); } System.out.println("=====================batchQuery======================"); } } | cs |
이 코드는 실무에서는 사용할 수없는 코드이다. 하지만 직관적으로 영속성 컨텍스트의 값을 수정배치쿼리와 동일하게 변경한것을 보여주기 위해 짠것이다. 이 예제의 결과는 수정배치에서 변경된 값과 동일한 엔티티가 반환되게 된다.
'Web > JPA' 카테고리의 다른 글
JPA - LazyInitializationException No Session(다중 스레드 트랜잭션,Multi Thread Transaction) (1) | 2019.05.29 |
---|---|
Spring JPA - JPA cascade 란?! (0) | 2019.04.29 |
JPA - JPAQuery와 Q클래스 (0) | 2019.02.11 |
JPA - NativeQuery ( SQL ) 네이티브 SQL 사용하기! (0) | 2019.02.11 |
JPA - QueryDSL, JPQL Query Builder Class (0) | 2019.02.11 |