💡다형성 쿼리
- Item : 부모객체
- Album, Movie, Book : 자식 객체
📗TYPE
- 조회 대상을
특정 자식으로 한정
- 예) Item 중 Book, Movie를 조회하라
select i from Item i where type(i) IN(Book, Movie)
select i from Item i where i.DTYPE in('B', 'M');
📗TREAT(JPA 2.1)
- 자바의
타입 캐스팅과 유사
(형변환)
- 상속 구조에서 부모 타입을 특정 자신 타입으로 다룰 때 사용
- FROM, WHERE, SELECT(하이버네이트 지원) 사용
- 예) 부모인 Item과 자식 Book이 있다.
select from Item i where treat(i as Book).author = 'kim';
select i.* from Item i where i.DTYPE = 'B' and i.author = 'kim';
💡엔티티 직접 사용
기본 키 값
- JPQL에서 엔티티를 직접 사용하면 SQL에서
해당 엔티티의 기본 키
값을 사용
select count(m.id) from Member m
select count(m) from Member m
select count(m.id) as cnt from Member m
- 파라미터로 엔티티를 넘겨주거나 식별자를 직접 전달하더라도 실행되는 SQL은 같다.
String jpql = "select m from Member m where m = :member";
List resultList = em.createQuery(jpql)
.setParameter("member", member)
.getResultList();
String jpql = "select m from Member m where m.id = :memberId";
List resultList = em.createQuery(jpql)
.setParameter("memberId", memberId)
.getResultList();
select m.* from Member m where m.id = ?
📗외래 키 값
Team team = em.find(Team.class, 1L);
String query = "select m from Member m where m.team = :team";
List resultList = em.createQuery(query)
.setParameter("team", team)
.getResultList();
String query = "select m from Member m where m.team.id = :teamId";
List resultList = em.createQuery(query)
.setParameter("teamId", teamId)
.getResultList();
select m.* from Member m where m.team_id = ?
💡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();
📌XML에 정의
//[META_INF?persistence.xml]
<persistence-unit name="jpabook">
<mapping-file>META-INF/ormMember.xml</mapping-file>
//[META-INF/ormMember.xml]
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="htt://xmlns.jcp.org/xml/ns/persistence/orm" version="2.1">
<named-query name="Member.findByUsername">
<query>
<![CDATA[ select m from Member m where m.username = :username]]
</query>
</named-query>
</entity-mappings>
- 어노테이션과 XML에 정의된 Named 쿼리는 XML이 항상 우선권을 가진다.
➡️애플리케이션 운영환경에 따라 다른 XML을 배포할 수 있다.
💡벌크 연산
- SQL의 update/delete 문을 생각하자
- 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
- JPA의 변경 감지 기능으로 실행하려면 너무 많은 SQL이 실행되어야 한다.
- 재고가 10개 미만인 상품을 리스트로 조회
- 상품 엔티티의 가격을 10% 증가
- 트랜잭션 커밋 시점에 변경 감지 동작
- 변경된 데이터가 100건이라면 100번의 update SQL 실행
📗벌크연산 예제
- 쿼리 한번으로 여러 테이블 로우 변경(엔티티)
- exeuteUpdate()의 결과는 영향받은 엔티티 수 반환
- UPDATE, DELETE 지원
- INSERT(insert into .. select, 하이버네이트 지원)
String query = "update Product p "+
"set p.price = p.price * 1.1 where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(qlString)
.setParameter("stockAmount", 10)
.executeUpdate();
📗벌크 연산 주의점
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
- 벌크 연산을 먼저 실행
- 벌크 연산 수행 후 영속성 컨텍스트 초기화
Member member1 = new Member();
member1.setUsername("회원1");
member1.setAge(0);
em.persist(member1);
Member member2 = new Member();
member2.setUsername("회원2");
member2.setAge(0);
em.persist(member2);
Member member2 = new Member();
member1.setUsername("회원2");
member2.setAge(0);
em.persist(member2);
int resultCount = em.createQuery("update Member m = set m.userage=20")
.executeUpdate();
System.out.println("member1 = " + member1.getAge());
System.out.println("member2 = " + member1.getAge());
System.out.println("member3 = " + member1.getAge());
- 벌크 연산이 flush를 하는 시점에는 나이가 다 0임
- 벌크연산 직전에 자동 fulsh -> DB에 0으로 저장됨
- 그 후 벌크연산(UPDATE)가 나가서 20살로 업데이트됨
➡️영속성 컨텍스트에 저장되지 않고 데이터베이스에 바로 쿼리가 나간다
- 영속성 컨텍스트에 저장되지 않으므로 get.Age()로 멤버의 나이를 출력하면 0으로 출력된다.
즉, 벌크 연산 수행 후 영속성 컨텍스트 초기화를 해주는 것이 좋다.
int resultCount = em.createQuery("update Member m = set m.userage=20")
.executeUpdate();
em.clear();
Member findMember - em.find(Member.class, member1.getId());
System.out.println("member1 = " + member1.getAge());