Entity, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)이 대상이다.
distinct를 붙여서 중복 제거를 할 수 있다.
Entity 프로젝션
select m from Member m
select m.team from Member m
m이 Member Entity의 alias이므로 Entity 프로젝션이다.
m.team도 결과가 team Entity이므로 Entity 프로젝션에 해당한다.
public class JpaMain {
public static void main(String[] args) {
Member member = new Member();
member.setUsername("member");
em.persist(member);
em.flush();
em.clear();
// Entity 프로젝션으로 조회한 데이터는 영속성 컨텍스트에서 관리된다.
List<Member> result = em.createQuery("select m from Member m", Member.class).getResultList();
Member findMember = result.get(0);
// 영속성 컨텍스트에서 관리되기 때문에 update 쿼리가 나간다.
findMember.setAge(30);
tx.commit();
}
}
Entity 프로젝션을 하면 select에 들어가는 모든 데이터가 영속성 컨텍스트에서 관리된다.
public class JpaMain {
public static void main(String[] args) {
Member member = new Member();
member.setUsername("member");
em.persist(member);
em.flush();
em.clear();
List<Team> result = em.createQuery("select m.team from Member m", Team.class).getResultList();
tx.commit();
}
}
코드는 단순 select 문이지만 m.team이 다른 테이블이므로 실제로는 join으로 나간다.
이런 경우에는 그냥 join으로 명확히 써주는 것이 좋다.
select t from Member m join m.team t
안 그러면 join이 발생하는지 예측이 안된다.
임베디드 타입 프로젝션
select m.address from Member m
address 같은 임베디드 타입도 가능하다.
public class JpaMain {
public static void main(String[] args) {
Member member = new Member();
member.setUsername("member");
em.persist(member);
em.flush();
em.clear();
// 임베디드 타입 조회
em.createQuery("select o.address from Order o", Address.class).getResultList();
tx.commit();
}
}
임베디드 타입은 그 Entity에 속해있으므로 join 없이 조회한다.
임베디드 타입은 소속된 타입이므로 Entity 조회하듯 할 수 없다.
ex. select address from Address (X)
스칼라 타입 프로젝션
select m.username, m.age from Member m
String, int 등의 기본 데이터 타입이 해당한다.
여러 값 조회
select m.username, m.age
from Member m
조회하는 데이터들의 타입이 다르거나 여러 개를 가져와야 할 때는 3가지 방법을 사용할 수 있다.
Query 타입으로 조회
public class JpaMain {
public static void main(String[] args) {
// String과 int 값을 반환한다.
Query query = em.createQuery("select m.username, m.age from Member m");
}
}
Object[] 타입으로 조회
public class JpaMain {
public static void main(String[] args) {
Member member = new Member();
member.setUsername("member");
member.setAge(30);
em.persist(member);
List resultList = em.createQuery("select m.username, m.age from Member m")
.getResultList();
Object o = resultList.get(0);
Object[] result = (Object[]) o;
System.out.println("result = " + result[0]);
System.out.println("result = " + result[1]);
}
}
Object타입으로 바꿔서 조회한다.
public class JpaMain {
public static void main(String[] args) {
Member member = new Member();
member.setUsername("member");
member.setAge(30);
em.persist(member);
List<Object[]> resultList = em.createQuery("select m.username, m.age from Member m")
.getResultList();
Object[] result = resultList.get(0);
System.out.println("result = " + result[0]);
System.out.println("result = " + result[1]);
}
}
이렇게 제네릭을 이용해 바로 받아올 수도 있다.
new 명령어로 조회
public class JpaMain {
public static void main(String[] args) {
Member member = new Member();
member.setUsername("member");
member.setAge(30);
em.persist(member);
em.flush();
em.clear();
// 단순 값을 DTO로 바로 가져올 수도 있다.
List<MemberDto> resultList = em
// 패키지명.생성자로 불러온다.
.createQuery("select new jpql.MemberDto(m.username, m.age) from Member m",
MemberDto.class).getResultList();
MemberDto memberDto = resultList.get(0);
System.out.println("result = " + memberDto.getUsername());
System.out.println("result = " + memberDto.getAge());
tx.commit();
}
}
public class MemberDto {
private String username;
private int age;
// 생성자의 순서와 타입이 일치해야 가져올 수 있다.
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}