JPA

[JPA] 28. 기타(다형성 쿼리, 엔티티 직접 사용, Named 쿼리, 벌크 연산)

inuma 2021. 2. 16. 23:38
프로젝트 git url github.com/rtef23/JpaStudy
필요 1. docker
2. docker-compose
프로젝트 정보 mysql : 5.7 - docker image
java : 1.8

 

다형성 쿼리

TYPE

  • 조회 대상을 특정 자식으로 한정
//Item 중에 Book, Movie를 조회해라
//[JPQL]
SELECT i
  FROM Item i
 WHERE TYPE(i) IN (Book, Movie)

//[SQL]
SELECT i
  FROM Item i
 WHERE i.DTYPE IN ('B', 'M')

 

TREAT (JPA 2.1)

  • 특정 타입의 자식을 대상으로 하는 쿼리
//[JPQL]
SELECT i
  FROM Item i
 WHERE TREAT(i as Book).author = 'kim'

//[SQL]
SELECT i.*
  FROM Item i
 WHERE i.DTYPE = 'B'
   AND i.author = 'kim'

 

엔티티 직접 사용 - 기본키

  • JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키값을 사용
//[JPQL]
SELECT COUNT(m.id)
  FROM Member m
  
SELECT COUNT(m)
  FROM Member m

//[SQL](위 2개의 JPQL모두 아래의 쿼리로 변환된다.)
SELECT COUNT(m.id)
  FROM Member m
//[JPQL]
SELECT m
  FROM Member m
 WHERE m = :member

SELECT m
  FROM Member m
 WHERE m.id = :memberId

//[SQL](위 2개의 JPQL모두 아래의 쿼리로 변환된다.)
SELECT m.*
  FROM Member m
 WHERE m.id = ?

 

엔티티 직접 사용 - 외래키

//[JPQL]
SELECT m
  FROM Member m
 WHERE m.team = :team
 
SELECT m
  FROM Member m
 WHERE m.team.id = :teamId
 
//[SQL](위 2개의 JPQL모두 아래의 쿼리로 변환된다.)
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> results = entityManager
  .createNamedQuery("Member.findByUsername", Member.class)
  .setParameter("username", "회원-1")
  .getResultList();

 

벌크 연산

  • 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
    • JPA의 변경 감지 기능으로 실행하려면 너무 많은 SQL이 실행된다.
      1. 재고가 10개 미만인 상품 리스트 조회
      2. 상품 엔티티의 가격을 10% 증가
      3. 트랜잭션 커밋 시점에 변경감지 동작
  • 쿼리 1번으로 테이블의 여러 row를 변경
  • UPDATE, DELETE 지원
Product product1 = new Product();
product1.setPrice(10);

String query = "update Product p" + 
               "   set p.price = p.price * 1.1" +
               " where p.stockAmount < :stockAmount";

//JPA에서 flush를 자동 호출하게 된다.
int resultCount = entityManager
  .createQuery(query)
  .setParameter("stockAmount", 10)
  .executeUpdate();
  
//product1.price = 10로 벌크 연산 처리된 내용이 반영되지 않는다.
System.out.println("product1.price = " + product1.getPrice());


//위 이슈를 해결하기 위해서 clear API를 호출한다.(spring-data-jpa에서는 @Modifying으로 해결 가능)
entityManager.clear();

Product findProduct = entityManager.find(Product.class, product1.getId());

//product1.price = 11로 벌크 연산 처리된 내용이 반영된 내용으로 조회된다.
System.out.println("findProduct.price = " + findProduct.getPrice());

주의해야 할점

  • 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리하는 방식이기 때문에
    영속성 컨텍스트에서 관리하는 엔티티와 실제 데이터가 맞지 않는 경우가 있다.
    • 벌크 연산을 먼저 수행하는 방법으로 해결
    • 벌크 연상 수행후 영속성 컨텍스트를 초기화하는 방법으로 해결

 

'JPA' 카테고리의 다른 글

[JPA] 27. Fetch 조인 - 한계  (0) 2021.02.16
[JPA] 26. Fetch 조인 - 기본  (0) 2021.02.11
[JPA] 25. 경로표현식  (0) 2021.02.11
[JPA] 24. JPQL - 타입 표현, 기타식  (0) 2021.01.31
[JPA] 23. JPQL - 조인, 서브 쿼리  (0) 2021.01.31