본문 바로가기
JPA/JPQL

[JPQL] 다형성 쿼리, 엔티티 직접 사용, Named 쿼리, 벌크 연산

by yoon_seon 2023. 4. 26.

📌 다형성 쿼리

Album, Movie, Book이 Item을 상속받는 구조 설계.

 

Type

  • 조회 대상을 특정 자식으로 한정
  • ex. 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')

 

TREAT(JPA 2.1)

  • 자바 타입 캐스팅과 유사
  • 상속구조에서 부모타입을 특정 자식타입으로 다룰 때 사용
  • FROM, WHERE, SELECT(Hibernate 지원) 사용
  • ex. 부모인 Item과 자식 Book이 있다.
    [JPQL]
    select i from Item i where treat(i as Book).auther = 'kim'
    [SQL]
    select i from i where i.DTYPE IN ('B') and i.auther = 'kim'

 

 

📌 엔티티 직접 사용

엔티티 직접사용 - 기본 키 값

  • JPA에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용
    [JPQL]
    select count(m.id) from Member m // 엔티티의 아이디를 사용
    select count(m) from Member m // 엔티티를 직접사용  기본 키 값을 사용한다.
    [SQL]
    select count(m.id) as cnt from Member m

  • 엔티티를 파라미터나 식별자로 직접 전달
// 엔티티를 파라미터로 전달
String query = "select m from Member m where m = :member";
List resultList = em.createQuery(query)
                    .setParameter("member", member)
                    .getResultList();

// 식별자를 직접 전달
String query = "select m from Member m where m.id = :memberId";
List resultList = em.createQuery(query)
                    .setParameter("memberId", memberId)
                    .getResultList();

실행 된 SQL

select m.* from Member m where m.id = ?

 

엔티티 직접사용 - 외래키 값

  • JPA에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용
Team teamA = em.find(Team.class, 1L);

String query = "select m from Member m where m.team = :team";
List resultList = em.createQuery(query)
                    .setParameter("team", teamA)
                    .getResultList();

String query = "select m from Member m where m.team.id = :teamId";
List resultList = em.createQuery(query)
                    .setParameter("teamId", teamId) // Team 엔티티에 설정되있는 외래키명
                    .getResultList()

실행된 SQL

select m.* from Member m where m.team_id = ?

 

 

 

📌 Named 쿼리

  • 미리 정의해서 이름을 부여해 두고 사용하는 JPQL
  • 정적쿼리만 가능
  • 어노테이션, XML에 정의한다.

어노테이션 정의

// ...
@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>
  • 사용법은 @NamedQuery의 query를 사용방법과 같다.
  • Xml이 항상 우선권을 가진다.
  • 애플리케이션 운영 환경에 따라 다른 Xml을 배포할 수 있다.
  • 애플리케이션 로딩 시점에 초기화 후 재사용
  • 애플리케이션 로딩 시점에 @NamedQuery 지정해둔 쿼리 파싱하여 검증

 

참고 : SpringData JPA를 사용한다면 이미 NamedQuery를 사용하고 있는 것이다.

@Repository
public interface MemberRepository extends JpaRepository<Member, Long>{

	@Query("select u from User u where u.username = ?1")
	Member findByUsername(String username);
}

@Repository annotation이 등록된 인터페이스에서 사용되는 @Query annotation에 있는 JPQL(or native)들이 NamedQuery로써 컴파일시에 등록되는 것이다.

 

 

📌 벌크 연산

  • 벌크연산은 우리가 알고있는 SQL의 UPDATE, DELETE 문이라고 생각하면 된다.
    ex. 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
  • 만약 JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL이 실행되어야 한다.
    1. 재고가 10개 미만인 상품을 리스트로 조회한다.
    2. 상품 엔티티의 가격을 10% 증가한다.
    3. 트랜잭션 커밋 시점에 변경감지가 동작한다.
  • 변경된 데이터가 100건이라면 UPDATE SQL 100번 실행되야 한다.

 

벌크 연산 예제

  • 쿼리 한 번으로 여러 테이블 로우 변경(엔티티)
  • executeUpdate()의 결과는 영향받은 엔티티 수 반환
  • UPDATE, DELETE 지원
  • INSERT(insert into ... select, 하이버네이트 지원)
int resultCount = em.createQuery("update Member m set m.age = 20")
                    .executeUpdate();

System.out.println("result : "+resultCount); // 영향을 받은 쿼리 count

 

벌크 연산 주의

  • 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리 한다.
  • 벌크 연산을 실행하면 flush 된다. 
    • 벌크 연산을 먼저 실행한다.
    • 벌크 연산 수행 후 영속성 컨텍스트 초기화한다.( em.clear() )
      : 이미 영속성 컨텍스트에 올라가 있는 엔티티 존재하는데  벌크 연산이 실행되면서 flush가 되어 버리면 DB와 객체가 싱크가 맞지 않기 때문에 벌크 연산을 수행 후 영속성 컨텍스트를 초기화하여 싱크를 맞춘다.

 

참고 : SpringData JPA를 사용한다면  @Modifying을 통해 벌크연산이 가능하다.

 

 

 


해당 글은 인프런의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 강의를 정리한 내용입니다.

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

댓글