본문 바로가기
Study/JPA

객체지향 쿼리 언어) Named 쿼리, 벌크 연산

by _비니_ 2024. 7. 30.

📌 Named 쿼리 - 정적 쿼리

  • 미리 정의해서 이름을 부여해두고 사용하는 JPQL
    • 자주 사용되는 쿼리를 표준화하고, 코드의 가독성과 유지보수성을 높이기 위한 방법
  • 정적 쿼리
  • 어노테이션, XML에 정의
  • 애플리케이션 로딩 시점에 초기화 후 재사용🌟
    • 쿼리가 실행될 때마다 컴파일되는 동적 쿼리와 다름!!!!
  • 애플리케이션 로딩 시점에 쿼리를 검증🌟🌟
    • 문법 오류를 사전에 발견할 수 있음

 

//Member 엔터티 클래스에 @NamedQuery 어노테이션을 사용하여 쿼리를 정의
@Entity
@NamedQuery(
        name = "Member.findByUsername",
        query="select m from Member m where m.username = :username") 
public class Member {
    ... 
}


// 쿼리를 실행할 때 createNamedQuery 메서드를 통해 호출
List<Member> resultList =
  em.createNamedQuery("Member.findByUsername", Member.class) 
        .setParameter("username", "회원1")
        .getResultList();

 

 

 

Spring Data JPA에서는?! (이름없는 Named Query라고 부름?)

⇒ 인터페이스 메서드 위에 바로 선언

public interface UserRepository extends JpaRepository<User, Long> {
	
	@Query("select u from U/ser u where u.emailAddress = ?1")
	User findByEmailAddress(String emailAddress);
}
  • JPA가 @Query("select u from U/ser u where u.emailAddress = ?1" 이를 Named Query로 등록한다고 보면 됨 (?)
    • Named 쿼리는 엔티티 클래스 또는 XML 파일에 정의되어 여러 리포지토리에서 재사용할 수 있는 반면, @Query는 특정 메서드에 대해 정의되며 재사용이 불가능
  • 애플리케이션 로딩 시점에 이를 파싱함 ⇒ 문법 오류를 다 잡아줌
실무에서는 Spring Data JPA를 섞어 사용하는 것이 좋음 🌟

 

 

📌 JPQL - 벌크 연산

  • 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
  • JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL 실행
    1. 재고가 10개 미만인 상품을 리스트로 조회한다.
    2. 상품 엔티티의 가격을 10% 증가한다.
    3. 트랜잭션 커밋 시점에 변경감지가 동작한다.
  • 변경된 데이터가 100건이라면 100번의 UPDATE SQL 실행

 

📌 벌크 연산 예제

  • 쿼리 한 번으로 여러 테이블 로우 변경(엔티티)
int resultCount = em.createQuery("update Member m set m.age = 20")
	.executeUpdate(); //영향받은 엔티티 수 반환 => 3
	
System.out.println("resultCount = " + resultCount);

>> 모든 회원의 나이가 20살로 변경됨

  • executeUpdate()의 결과는 영향받은 엔티티 수 반환
  • UPDATE, DELETE 지원
  • INSERT(insert into .. select, 하이버네이트 지원)

 

🚨 벌크 연산 주의 🚨

  • 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리

<해결책 2가지 (연속된 해결책 아님)>

  1. 벌크 연산을 먼저 실행
    1. 이러면 영속성 컨텍스트에 아무것도 없으니 O
  2. 벌크 연산 수행 후 영속성 컨텍스트 초기화

 

Member member1 = new Member();
member1.setUser("회원1");
member.setTeam(teamA);
em.persist(member1);

Member member2 = new Member();
member2.setUser("회원2");
member.setTeam(teamA);
em.persist(member2);

Member member3 = new Member();
member3.setUser("회원3");
member.setTeam(teamB);
em.persist(member3);

//지금 member1,2,3가 영속성 컨텍스에 들어있음

//벌크 연산으로 DB를 강제로 업데이트함 => 영속성 컨텍스트에 반영 안되어있음
//DB에는 

//Flush
int resultCount = em.createQuery("update Member m set m.age = 20")
	.executeUpdate(); //영향받은 엔티티 수 반환 => 3
	
System.out.println("resultCount = " + resultCount);
	
System.out.println("member1.getAge() = " + member1.getAge());
System.out.println("member2.getAge() = " + member2.getAge());
System.out.println("member3.getAge() = " + member3.getAge());

 

>> DB에만 반영이 되어있는 것.

 

20살로 반영 안돼있음

 

int resultCount = em.createQuery("update Member m set m.age = 20")
	.executeUpdate(); //영향받은 엔티티 수 반환 => 3
	
em.clear();
Member findMember = em.find(Member.class, member1.getId());
  • em.clear() 후 em.find를 하면 영속성 컨텍스트가 초기화된 상태에서 다시 조회하므로 20살로 조회가 됨

 

반응형

'Study > JPA' 카테고리의 다른 글

객체지향 쿼리 언어) JPQL - 페치 조인(fetch join)  (0) 2024.07.30
객체지향 쿼리 언어) JPQL - 경로 표현식  (0) 2024.07.30
JPQL  (0) 2024.07.27
조인과 서브쿼리  (0) 2024.07.27
프로젝션과 페이징  (0) 2024.07.27