FireDrago

[JPA] JPQL 기본문법 1 본문

프로그래밍/JPA

[JPA] JPQL 기본문법 1

화이용 2024. 4. 4. 12:05

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. 조인 대상 필터링

회원과 팀을 조회하면서, 팀 이름이 A인 팀만 조인

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 크기

 

 

<사용자 정의 함수 설정>

 

  1. 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