객체지향 쿼리 언어10 Advanced - 엔티티직접사용
- 경로표현식
- 페치 조인 1 - 기본
- 페치 조인 2 - 한계
- 다형성 쿼리
- 엔티티 직접 사용
- Named 쿼리
- 벌크 연산
TL; DR
Django | Spring |
---|---|
Article.objects.filter(user=user) Article.objects.filter(user_id=user.id) |
String jpql = “SELECT m FROM Member m WHERE m = :member”; String jpql = ‘SELECT m FROM Member m WHERE m.id = :memberId’ |
01. 엔티티 직접 사용 - 기본 키 값
- Entity 를 파라미터로 전달
String jpql = "SELECT m FROM Member m WHERE m = :member";
List resultList = em.createQuery(jpql)
.setParameter("member", member)
.getResultList();
- 식별자(id) 직접 전달
String jpql = "SELECT m FROM Member m WHERE m.id = :memberId";
List resultList = em.createQuery(jpql)
.setParameter("memberId", member.getId())
.getResultList();
실행된 SQL
SELECT m.*
FROM Member m
WHERE m.id = ?
02. 엔티티 직접 사용 - 외래키 값
Team team = em.find(Team.class, 1L);
// 1
String qlString = "SELECT m FROM Member m WHERE m.team = :team";
List resultList = em.createQuery(qlString)
.setParameter("team", team)
.getResultList();
// 2
String qlString = "SELECT m FROM Member m WHERE m.team.id = :teamId";
List resultList = em.createQuery(qlString)
.setParameter("teamId", teamId)
.getResultList();
실행된 SQL
SELECT m.*
FROM Member m
WHERE m.team_id = ?
JPQL 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();
01. Named 쿼리 - XML 에 정의
# [META-INF/persistence.xml]
<persistence-unit name="jpabook">
<mapping-file>META-INF/ormMember.xml</mapping-file>
# [META-INF/ormMember.xml]
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" version="2.1">
<named-query name="Member.findByUsername">
<query>
SELECT m FROM Member m WHERE m.username= :username
</query>
</named-query>
<named-query name="Member.count">
<query>
SELECT count(m) FROM Member m
</query>
</named-query>
</entity-mappings>
추후 Spring-Data-JPA 를 사용하게되면, 아래와 같이 namedQuery 대신 쓸 수 있다.
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
}
벌크연산 (Bulk)
- 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
- JPA 변경 감지 기능(더티체킹) 으로 실행하려면 너무 많은 SQL 실행
- 재고가 10개 미만인 상품을 리스트로 조회한다.
- 상품 엔티티의 가격을 10% 증가한다.
- 트랜잭션 커밋 시점에 변경감지가 동작한다.
- 변경된 데이터가 100건이라면 100번의 Update SQL 실행
01. 벌크 연산 예제
- 쿼리 한번으로 여러 테이블 로우 변경(엔티티)
executeUpdate()
의 결과는 영향받은 엔티티 수 반환- Update, Delete 지원
- Insert (insert into … select, 하이버네이트 지원)
String jpql = "update Product p " +
"set p.price = p.price * 1.1 " +
"where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(jpql)
.setParameter("stockAmount", 10)
.executeUpdate();
02. 벌크 연산 주의
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
- 벌크 연산을 먼저 수행
- 벌크 연산 수행 후 영속성 컨텍스트 초기화
Member member1 = new Member();
member1.setUsername("member1");
member1.setAge(0);
em.persist(member1);
Member member2 = new Member();
member1.setUsername("member2");
member1.setAge(0);
em.persist(member2);
// flush
int resultCount = em.createQuery("update Member m set m.age = 20")
.executeUpdate();
em.clear(); // 영속성 컨텍스트를 초기화
Member findMember = em.find(Member.class, member1.getId());
System.out.println("findMember = " + findMember.getAge()); // 20 (em.clear()를 안해주면 0이 나옴)
댓글남기기