FireDrago
[JPA] JPQL 기본문법 1 본문
JPA는 객체중심으로 개발을 한다.
그래서 쿼리역시 엔티티 객체를 대상으로 하는 쿼리가 필요하다
이것이 JPQL 이다.
JPQL은 엔티티 객체를 대상으로 한 쿼리이며, SQL로 번역된다.
JPQL의 기본 문법과 기능을 알아보자
<JPQL 문법>
select m from Member as m where m.age > 18
- 엔티티와 속성은 대소문자 구분한다. (Member, age)
- JPQL 키워드는 대소문자 구분하지 않는다. (SELECT, FROM, where...)
- 별칭은 필수 (m ) as는 생략가능
<집합과 정렬>
select
COUNT(m), //회원수
SUM(m.age), //나이 합
AVG(m.age), //평균 나이
MAX(m.age), //최대 나이
MIN(m.age) //최소 나이
from Member m
[GROP BY 절]
[Having 절]
[ORDER BY 절]
<TypeQuery, Query>
// 반환 타입이 명확할 때 (TypeQuery< >)
TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
TypedQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);
// 반환 타입이 명확하지 않을 때 (Query)
Query query = em.createQuery("select m.username, m.age from Member m");
TypeQuery : 반환 타입이 명확할 때 사용
Query : 반환 타입이 명확하지 않을 때 사용
<결과 조회 API>
// getResultList() : 결과없으면 빈 리스트 반환 (null 걱정 x)
List<Member> resultList = em.createQuery("select m from Member m", Member.class)
.getResultList();
for (Member member1 : resultList) {
System.out.println("member1 = " + member1);
}
// getSingleResult( ) -> 반환값 하나일때 , 결과없거나, 둘 이상이면 예외발생
Member singleResult = em.createQuery("select m from Member m where m.id = 1", Member.class)
.getSingleResult();
query.getResultList(): 결과가 하나 이상일 때, 리스트 반환
• 결과가 없으면 빈 리스트 반환
query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환
• 결과가 없으면: javax.persistence.NoResultException
• 둘 이상이면: javax.persistence.NonUniqueResultException
<파라미터 바인딩>
// 파라미터 바인딩
em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", "member1");
// 위치로 바인딩 -> 사용하지 말자 , 오류 가능성 있다.
em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter(1, "member1");
파라미터 바인딩은 이름으로 바인딩 하자
위치 바인딩은 오류의 위험성이 높다
<프로젝션>
// 엔티티 프로젝션 -> 영속성 관리 됨
List<Team> result = em.createQuery("select t from Member m join m.team t", Team.class)
.getResultList();
// 임베디드 타입 프로젝션 (엔티티로부터 시작해야 함)
List<Address> embedded = em.createQuery("select o.address from Order o", Address.class)
.getResultList();
// 스칼라 타입 프로젝션 (기본 데이터 타입)
em.createQuery("select distinct m.username from Member m")
.getResultList();
프로젝션 : SELECT 절에 조회할 대상을 지정하는 것
- 엔티티, 임베디드 타입, 스칼라 타입
<프로젝션 - 여러값 조회>
// 1-1 Object[] 로 여러가지 값 조회
List resultList1 = em.createQuery("select distinct m.username, m.age from Member m")
.getResultList();
Object o = resultList1.get(0);
Object[] objArray = (Object[]) o;
System.out.println("username = " + objArray[0]);
System.out.println("age = " + objArray[1]);
// 1-2 제네릭 Object[] 로 여러가지 값 조회
List<Object[]> resultList2 = em.createQuery("select distinct m.username, m.age from Member m")
.getResultList();
// 1-3 new 명령어로 여러가지 값 조회
List<MemberDTO> resultList3 =
em.createQuery("select distinct new jpql.MemberDTO (m.username, m.age) from Member m", MemberDTO.class)
.getResultList();
============================================================
public class MemberDTO {
private String usernaeme;
private int age;
public MemberDTO(String usernaeme, int age) {
this.usernaeme = usernaeme;
this.age = age;
}
public String getUsernaeme() {
return usernaeme;
}
public void setUsernaeme(String usernaeme) {
this.usernaeme = usernaeme;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Object 타입으로 여러 타입을 조회하는 방법과
new 명령어로 조회하는 방법이 있다.
- DTO로 바로 조회하기 위해서 new 생성자를 SELECT 문에 사용한다.
- 패키지 명을 포함한 전체 클래스명을 입력해야한다.
- DTO 에 생성자가 존재해야 한다.
<페이징>
// 페이징 기능 -> DB에 맞게 쿼리를 만들어서 페이징 (추상화)
em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
페이징 기능을 추상화 하여 setFirstResult() , setMaxResult() 단 두가지의 메서드로 페이징 기능을 사용할 수 있다.
DB 마다 적절한 페이징 쿼리가 실행된다.
<조인>
1. 조인 대상 필터링

2. 연관관계 없는 엔티티 외부조인

<JPQL 타입 표현>
// Enum 조회시에는 패키지명을 포함해야한다.
String query = "select m.username, 'HELLO', true from Member m" +
"where m.type = jpql.MemberType.ADMIN";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
| 종류 | 표현 |
| 문자 | 'Hello', 'She' |
| 숫자 | 10L (Long), 10D (Double), 10F (Float) |
| Boolean | true, false (대소문자 구분안함) |
| ENUM | jpabook.MemberType.ADMIN (패키지 포함) |
| 엔티티타입 | TYPE(m) = Member (상속 관계에서 사용) |
<조건식, 기본함수>
String query =
"select" +
"case when m.age <= 10 then '학생요금' " +
" when m.age >= 60 then '경로요금' " +
" else '일반요금' " +
"end" +
"from Member m";
em.createQuery(query);
<기본 함수>
| CONCAT | 문자열 합치기 |
| SUBSTRING | 문자열 자르기 |
| TRIM | 문자열 공백 제거 |
| LOWER, UPPER | 소문자, 대문자 변환 |
| LENGTH | 길이 |
| LOCATE | 위치 숫자로 반환 |
| ABS | 절대값 |
| SQRT | 제곱근 |
| MOD | 두 숫자의 나눗셈 |
| SIZE | 크기 |
<사용자 정의 함수 설정>
- FunctionContributer의 구현체를 만들어 준다.
package custom;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.FunctionContributor;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StandardBasicTypes;
public class CustomFunctionContributor implements FunctionContributor {
@Override
public void contributeFunctions(FunctionContributions functionContributions) {
functionContributions.getFunctionRegistry()
.register("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
}
}
2. src/main/resources/META-INF/services/org.hibernate.boot.model.FunctionContributor 파일을 생성한다.
3. 해당 파일에 직접 구현한 CustomFunctionContributor를 등록한다.


- 패키지명.컨트리뷰터이름 형태로 등록!!
- custom.CustomFunctionContributor
4. 사용자 지정 함수를 사용한다.

'프로그래밍 > JPA' 카테고리의 다른 글
| [JPA] @PersistenceContext 사용해야 하는 이유 (0) | 2024.04.08 |
|---|---|
| [JPA] JPQL 기본문법 2 (0) | 2024.04.06 |
| [JPA] 연관관계 관리 (0) | 2024.04.01 |
| [JPA] 엔티티 매핑 (0) | 2024.03.27 |
| [JPA] 영속성 관리 (0) | 2024.03.26 |
