프로젝션
프로젝션이란? SELECT 절에 조회할 대상을 지정하는 것이다.
프로젝션 대상은 크게 세가지로 나눌 수 있는데, 엔티티 타입, 임베디드 타입, 스칼라 타입이 있다.
- SELECT m FROM Member m : 엔티티 프로젝션
- SELECT m.address FROM Member m: 임베디드 타입 프로젝션
- SELECT m.username, m.age FROM Member m: 스칼라 타입 프로젝션
예제를 통해 알아보자.
@Autowired
EntityManagerFactory emf;
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Team team1 = new Team();
team1.setName("teamA");
em.persist(team1);
Member member1 = new Member();
member1.setUsername("member1");
member1.setAge(20);
member1.setTeam(team1);
em.persist(member1);
List<Member> resultList = em.createQuery("select m from Member m", Member.class).getResultList();
만약 이런 코드가 있다고 할 때 List 안에 담긴 Member는 영속성 컨텍스트에 담길까?
em.flush();
em.clear();
Member findMember = resultList.get(0);
findMember.setAge(20);
영속성 컨텍스트를 Flush하고 clear을 해서 비운뒤 List에 담긴 Member의 나이를 변경해보았다.
Hibernate:
update
member m1_0
set
age=20
where
m1_0.age<=?
실행결과에서 볼 수 있듯이 업데이트 쿼리가 나가므로 엔티티 프로젝션으로 조회하면 모두 영속성 컨텍스트에 담긴다.
임베디드 타입은 말 그대로 엔티티 안에 선언 되어있는 임베디드를 조회하는 것이다.
// Order.java
@Entity
@Table(name = "ORDERS")
public class Order {
@Id @GeneratedValue
private Long id;
private int orderMount;
@Embedded
private Address address;
}
// 실제 실행 코드(임베디드 타입 조회)
List<Address> result = em.createQuery("select o.address from Order o ", Address.class).getResultList();
스칼라 타입
여러 필드가 나열 되어있을 때는 세가지 방법으로 조회할 수 있다.
1. Query 타입으로 조회
List resultList = em.createQuery("select m.username, m.age from Member m").getResultList();
Object o = resultList.get(0);
Object[] objects = (Object[]) o;
System.out.println("objects[0] = " + objects[0]);
System.out.println("objects[1] = " + objects[1]);
// 실행결과
objects[0] = member1
objects[1] = 20
2. Object[] 타입으로 조회
List<Object[]> resultList = em.createQuery("select m.username, m.age from Member m").getResultList();
Object[] objects = resultList.get(0);
System.out.println("objects[0] = " + objects[0]);
System.out.println("objects[1] = " + objects[1]);
// 실행결과
objects[0] = member1
objects[1] = 20
3. new 명령어로 조회
먼저 캐스팅할 class를 생성한다.
package jpa.practice.v0;
import lombok.Data;
@Data
public class MemberDTO {
private String username;
private int age;
public MemberDTO(final String username, final int age) {
this.username = username;
this.age = age;
}
public MemberDTO() {
}
}
그리고 new 연산자와 패키지 경로 까지 전체 class명을 입력한다 스칼라 타입을 담는다.
순서와 타입이 일치하는 생성자가 꼭 필요하다.
List<MemberDTO> resultList = em.createQuery("select new jpa.practice.v0.MemberDTO(m.username, m.age) from Member m", MemberDTO.class).getResultList();
MemberDTO memberDTO = resultList.get(0);
단점은 패키지명이 길어지면 관리나 읽기가 힘들어진다는 단점이 있다.
페이징
JPA는 페이징을 다음 두 API로 추상화한다.
- .setFirstResult(int startPosition): 조회 시작 위치
- .setMaxResult(int maxResult): 조회할 데이터 수
그냥 단순히 시작위치와 몇개를 조회할 건지만 넣어주면 JPA가 알아서 페이징 쿼리를 만들어준다.
for (int i = 0; i < 100; i++) {
Member member1 = new Member();
member1.setUsername("member" + i);
member1.setAge(i);
member1.setTeam(team1);
em.persist(member1);
}
em.flush();
em.clear();
List<Member> resultList = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
for (Member member : resultList) {
System.out.println("member.toString() = " + member.toString());
}
실행결과
Hibernate:
select
m1_0.id,
m1_0.age,
m1_0.team_id,
m1_0.username
from
member m1_0
order by
m1_0.age desc
offset
? rows
fetch
first ? rows only
DB에 맞게 페이징 쿼리가 실행되는 것을 볼 수 있다.
현재 H2 데이터베이스에 연결되어 있으므로 offset과 fetch문법이 실행됐다.
만약 DB을 변경하더라도 그에 맞게 실행될 것이다.
'JPA' 카테고리의 다른 글
[JPA] 페치조인과 일반 조인의 차이점 (1) | 2024.12.27 |
---|---|
[JPA] JPQL의 경로표현식이란? (0) | 2024.12.27 |
[JPA] JPA의 타입 (임베디드 타입, 값 타입, 컬렉션 타입)이란? (1) | 2024.12.20 |
[JPA] 프록시와 지연로딩, 즉시로딩 (0) | 2024.11.26 |
[JPA] 연관관계 매핑 (0) | 2024.11.25 |