JPA는 다양한 쿼리 방법을 지원한다.
JPA를 사용하면 엔티티 객체를 중심으로 개발할 수 있다.
JPA는 SQL을 추상화한 JPQL이라는 객체지향 쿼리 언어 제공
List<Member> result = em.createQuery(
"select m From Member m where m.username like '%kim%'", Member.class)
.getResultList();
SQL을 추상화했기 때문에 특정 DB SQL에 의존하지 않는다.
객체지향 SQL이다.
JPQL은 특정 DB에 종속되지 않는다.
JPQL 기본 문법은 다음과 같다.
select m from Member as m where m.age>18
m
은 필수이고 as는 생략가능하다집합과 정렬은 위와 같이 한다.
TypeQuery는 반환 타입이 명확할 때 사용하고 Query는 반환 타입이 명확하지 않을 때 사용한다
TypedQuery<Member> typedQuery = em.createQuery("select m from Member m", Member.class);
TypedQuery<String> typedQuery2 = em.createQuery("select m.username from Member m", String.class);
Query query = em.createQuery("select m.username, m.age from Member m");
세번째의 경우 Typed를 사용할 수 없고 그냥 Query를 사용해야 한다. username과 age를 select해서 타입이 정의된 게 아니기 때문이다.
반환 타입이 명확하지 않을 때는 Query를 사용한다.
결과가 컬렉션일 때는 getResultList()
를 사용한다.
결과가 없을 때는 빈 List를 반환한다.
TypedQuery<Member> typedQuery = em.createQuery("select m from Member m", Member.class);
List<Member> resultList = typedQuery.getResultList();
for (Member member1 : resultList) {
System.out.println("member1 = " + member1);
}
결과가 무조건 하나일 때는 getSingleResult()
를 사용한다.
TypedQuery<Member> typedQuery = em.createQuery("select m from Member m", Member.class);
Member singleResult = typedQuery.getSingleResult();
System.out.println("singleResult = " + singleResult);
하지만 singleResult()
를 호출했는데 결과가 안나오거나 여러개일 수 있다.
위와 같이 파라미터 바인딩을 할 수 있다.
위치 기반은 사용하지 않는 게 좋다(중간에 하나 추가하면 다 수정해야 하기 때문에)
Member singleResult = em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", "member1")
.getSingleResult();
System.out.println("singleResult = " + singleResult);
보통은 이런 식으로 메소드 chaining으로 편리하게 사용한다.
Select 절에 조회할 대상을 지정하는 것을 프로젝션이라고 한다.
프로젝션 대상은 4가지 경우가 있다.
select m from Member m
: 엔티티 프로젝션
select m.team from Member m
: 엔티티 프로젝션
select m.address from Member m
: 임베디드 타입 프로젝션
select m.username,m.age from Member m
: 스칼라 타입 프로젝션
스칼라 타입 프로젝션 인 경우 값을 어떻게 가져와야 할까?
1. Query 타입으로 조회
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("username = "+result[0]);
System.out.println("age = "+result[1]);
타입을 명기할 수 없어서 Obejct로 돌린다.
Object[]
타입으로 조회 List<Object[]> resultList = em.createQuery("select m.username, m.age from Member m ")
.getResultList();
Object[] result = resultList.get(0);
System.out.println("username = "+result[0]);
System.out.println("age = "+result[1]);
package jpashop;
public class MemberDTO {
private String username;
private int age;
public MemberDTO(String username, int age) {
this.username = username;
this.age = age;
}
}
위와 같이 값을 조회할 때 사용할 DTO를 만든다.
List<MemberDTO> result = em.createQuery("select new jpashop.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
.getResultList();
MemberDTO memberDTO = result.get(0);
System.out.println("memberDTO.getUsername() = " + memberDTO.getUsername());
System.out.println("memberDTO.getAge() = " + memberDTO.getAge());
selecct new jpashop.MemberDTO(m.usrname, m.age)
와 같이 패키지명까지 입력해서 new로 DTO로 받을 수 있다.
패키지명이 길면 다 적어야 한다는 단점이 있지만 제일 깔끔한 방법이다.
JPA 페이징은 다음 두 API로 추상화되어있다.
setFirstResult(int startPosition)
setMaxResult(int maxResult)
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
System.out.println("result.size() = " + result.size());
for (Member member1 : result) {
System.out.println("member1 = " + member1);
}
DB 방언에 맞춰 알맞은 쿼리가 나간다.