다형성 쿼리
- TYPE
- 조회 대상을 특정 자식으로 한정
• 예) Item 중에 Book, Movie를 조회해라
• [JPQL] select i
from Item i where type(i) IN (Book, Movie)
• [SQL] select i
from i where i.DTYPE in (‘B’, ‘M’) //DTYPE은 설정을 통해 미리 지정할 수 있음
- TREAT
• 자바의 타입 캐스팅과 유사
• 상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용
• FROM, WHERE, SELECT(하이버네이트 지원) 사용
• 예) 부모인 Item과 자식 Book이 있다.
• [JPQL] select i
from Item i where treat(i as Book).auther = ‘kim’
• [SQL] select i.*
from Item i where i.DTYPE = ‘B’ and i.auther = ‘kim’
엔티티 직접 사용
- 기본 키
• JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용
• [JPQL] select count(m.id) from Member m //엔티티의 아이디를 사용
select count(m) from Member m //엔티티를 직접 사용
• [SQL] (JPQL 둘다 같은 다음 SQL 실행)
select count(m.id) as cnt from Member m
- 외래 키
Named 쿼리 - 정적 쿼리
• 미리 정의해서 이름을 부여해두고 사용하는 JPQL
• 정적 쿼리
• 어노테이션, XML에 정의
• 애플리케이션 로딩 시점에 초기화 후 재사용
• 애플리케이션 로딩 시점에 쿼리를 검증 -> 오류를 더 잘 잡을 수 있게 됨
- 어노테이션에 정의
@Entity
@NamedQuery(
name = "Member.findByUsername",
query="select m from Member m where m.username = :username")
public class Member {
...
}
사용
List<Member> resultList =
em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "회원1")
.getResultList();
벌크 연산
• 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
• JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL 실행
• 1. 재고가 10개 미만인 상품을 리스트로 조회한다.
• 2. 상품 엔티티의 가격을 10% 증가한다.
• 3. 트랜잭션 커밋 시점에 변경감지가 동작한다.
• 변경된 데이터가 100건이라면 100번의 UPDATE SQL 실행
사용법
• 벌크 연산은 쿼리 한 번으로 여러 테이블 로우 변경(엔티티)
• executeUpdate()의 결과는 영향받은 엔티티 수 반환
• UPDATE, DELETE 지원
• INSERT(insert into .. select, 하이버네이트 지원)
예제
Member member1 = new Member();
member1.setUsername("member1");
em.persist(member1);
Member member2 = new Member();
member2.setUsername("member2");
em.persist(member2);
String query = "update Member m set m.age = 20";
int resultCount = em.createQuery(query).executeUpdate();
em.clear();
Member findMember = em.find(Member.class, member1.getId());
System.out.println("findMember.getAge() = " + findMember.getAge());
결과
findMember.getAge() = 20
주의할 점!
위의 코드에서 em.clear();를 주석처리하면 결과는 findMember.getAge() = 0이 됨.
이유는 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 날리기 때문.
그렇기 때문에 쿼리를 날리는 순간 영속성 컨텍스트에는 반영 전의 데이터가 남아있게 됨.
즉 db에는 20으로 업데이트가 되었지만 영속성 컨텍스트 안에는 여전히 age가 설정되지 않은 상태(0)으로 남아있음.
이러한 문제의 해결법은 2가지가 있다.
1. 영속성 컨텍스트에 값을 넣기 전에 벌크 연산을 먼저 실행
2. 벌크 연산 수행 후 영속성 컨텍스트 초기화
위의 코드는 2번 방법으로 문제를 해결함.
em.clear()를 통해 초기화를 해주면 em.find 시에 1차 캐시가 아닌 db에서 값을 가져옴.
------------------------------------------------------------------------------
*레퍼런스 : 인프런 김영한님 강의(자바 ORM 표준 JPA 프로그래밍)
댓글