"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
Named 쿼리
- 미리 정의해서 이름을 부여해두고 사용하는 JPQL 기능이다.
- 정적 쿼리이며 어노테이션, XML 에 정의해서 사용할 수 있다.
- 애플리케이션 로딩 시점에 초기화 후 재사용한다.
-> 정적 쿼리라서 변하지 않을 것이기 때문에 애플리케이션 로딩 시점에 JPA 나 Hibernate 같은 것들이 이를 SQL 로 파싱한다.
- 애플리케이션 로딩 시점에 쿼리를 검증한다.
생각해보면 단순히 쿼리를 재활용하는데 의미가 있는 듯 하나 정적 쿼리만으로 가능한 것에서 오는 큰 메리트가 있다.
그러나 Named 쿼리의 진짜 장점은 애플리케이션 로딩 시점에 쿼리를 검증한다는 것이다.
다음과 같이 쿼리에 이름을 부여할 수 있다.
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username" )
public class Member {
......
}
- JpaMain.java
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "회원1")
.getResultList();
- Hibernate SQL
Hibernate:
/* Member.findByUsername */ select
member0_.id as id1_0_,
member0_.age as age2_0_,
member0_.TEAM_ID as team_id5_0_,
member0_.type as type3_0_,
member0_.username as username4_0_
from
Member member0_
where
member0_.username=?
member = Member{id=3, username='회원1', age=0}
(XML 파일에 Named 쿼리를 정의하는 방법은 다음에 필요할 때 알아보도록 하자.)
Named 쿼리 환경에 따른 설정
- XML 이 항상 우선권을 가진다.
- 애플리케이션 운영 환경에 따라 다른 XML 을 배포할 수 있다.
그런데 사실 Spring Data JPA 에서는 JpaRepository 를 상속받는 Repository 인터페이스에서 @Query 어노테이션을 통해 Named 쿼리를 선언해 줄 수 있다.
(Spring Data JPA 는 JPA 를 편하게 사용하기 위해 추상화하는 역할을 수행한다.)
JPQL 벌크 연산
- 일반적으로 아는 update, delete 문에서, 한 건을 찍어 update 하거나 delete 하는 것을 제외한 나머지 모든 update, delete 문 이라고 생각하면 된다.
예시 : 재고가 10개 미만인 모든 상품의 가격을 10% 상승 시키려면?
JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL 을 실행시켜야 한다.
1. 재고가 10개 미만인 상품을 리스트로 조회한다.
2. 상품 Entity 의 가격을 10% 증가시킨다.
3. 트랜잭션 커밋 시점에 변경 감지가 동작한다.
- 변경된 데이터가 100 건이라면 100 번의 update SQL 이 실행되어야 한다.
* 벌크 연산이란?
쿼리 한 번으로 여러개의 update 를 제공하는 것을 벌크 연산이라고 한다.
- 쿼리 한 번으로 여러 테이블의 로우를 변경한다.(Entity)
- executeUpdate() 의 결과는 영향받은 Entity 수를 반환하는 것이다.
- update, delete 를 지원한다.
- insert(insert into ... select, Hibernate 지원) 또한 지원한다.
- JpaMain.java
int resultcount = em.createQuery("update Member m set m.age = 20")
.executeUpdate();
System.out.println("resultcount = " + resultcount);
- Hibernate SQL
Hibernate:
/* update
Member m
set
m.age = 20 */ update
Member
set
age=20
resultcount = 3
벌크 연산 사용시 주의할 점?
벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 전달한다.
방법1 : 벌크 연산을 먼저 실행한다.
방법2 : 벌크 연산 수행 후 영속성 컨텍스트를 초기화한다.
예를 들어 이런 경우가 발생할 수 있다.
- JpaMain.java
// JPQL 벌크 연산
int resultcount = em.createQuery("update Member m set m.age = 20")
.executeUpdate();
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());
- Hibernate SQL
Hibernate:
/* update
Member m
set
m.age = 20 */ update
Member
set
age=20
resultcount = 3
member1.getAge() = 0
member2.getAge() = 0
member3.getAge() = 0
위의 Hibernate SQL 을 보면, update 문을 통해 벌크 연산을 수행해서 DB 에 변경된 나이 값을 반영시켰음 에도 불구하고 각 멤버들의 나이를 곧바로 출력해본 결과 20살이 아닌 0 으로 출력되는 것을 확인할 수 있다.
이렇게 된 이유는 update 문을 통해 flush 가 발생하여 DB 에는 나이의 변경이 반영되었을 지라도 영속성 컨텍스트에는 그것이 반영되어 있지 않기 때문에, 영속성 컨텍스트에 나이가 반영되어 있지 않은 상태의 데이터가 계속 남아있게 되고(clear 를 하지 않으면 영속성 컨텍스트에 데이터가 계속 남아있게 된다.)
그로 인해 출력문을 이용해 영속성 컨텍스트에 있는 데이터를 읽어오면 나이의 변경이 반영되어 있지 않은 상태로 출력되는 것이다.
그렇기 때문에 벌크 연산을 수행하고 나면 왠만하면 영속성 컨텍스트를 초기화 해주는 것이 좋다.
-JpaMain.java
// JPQL 벌크 연산
int resultcount = em.createQuery("update Member m set m.age = 20")
.executeUpdate();
System.out.println("resultcount = " + resultcount);
em.clear(); // 영속성 컨텍스트 초기화
String query = "select m from Member m";
List<Member> member_list = em.createQuery(query, Member.class)
.getResultList(); // DB 의 데이터를 다시 영속성 컨텍스트로 가져온다.
for (Member member : member_list) {
System.out.println("member.getAge() = " + member.getAge());
}
- Hibernate SQL
Hibernate:
/* update
Member m
set
m.age = 20 */ update
Member
set
age=20
resultcount = 3
Hibernate:
/* select
m
from
Member m */ select
member0_.id as id1_0_,
member0_.age as age2_0_,
member0_.TEAM_ID as team_id5_0_,
member0_.type as type3_0_,
member0_.username as username4_0_
from
Member member0_
member.getAge() = 20
member.getAge() = 20
member.getAge() = 20
'JPA' 카테고리의 다른 글
JPQL - JPQL 의 여러가지 기능들(1) (0) | 2020.11.29 |
---|---|
JPQL - JPQL 페치 조인(4) (0) | 2020.11.28 |
JPQL - JPQL 페치 조인(3) (0) | 2020.11.28 |
JPQL - JPQL 페치 조인(2) (0) | 2020.11.28 |
JPQL - JPQL 페치 조인(1) (0) | 2020.11.28 |