본문 바로가기
Spring/JPA

JPQL - Query타입, 결과조회, 파라미터, 프로젝션, 페이징, 조인

by YoonJong 2022. 9. 27.
728x90

JPQL( Java Persistence Query Language )

 

JPQL 은 객체지향 쿼리 언어로, 테이블이 대상으로 하는 것이 아닌, 엔티티 객체를 대상으로 쿼리한다

JPQL 은 작성하고 실행하면 SQL 로 변환된다.

 

 

Member 은 엔티티이다.

Member m 처럼 별칭은 필수이다.

String jpql = "select m From Member m where m.name like ‘%hello%'";

TypeQuery : 반환 타입이 명확할 때 사용한다.

아래의 코드는 Member.class 로 반환타입이 명확하다.

TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);

 

Query : 반환 타입이 명확하지 않을때 사용한다.

아래 코드는 m.username 은 String 타입, m.age는 Integer 타입으로 반환타입이 명확하지 않다.

Query query = em.createQuery("SELECT m.username, m.age from Member m");

query.getResultList() : 결과가 하나 이상일 때, 리스트를 반환한다.

 - 결과가 없으면 빈 리스트를 반환한다. 예외를 발생하지 않으며, 컬렉션타입으로 NullPointException이 발생하지 않는다.

List<Member> resultMany = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println("resultMany = " + resultMany);
Hibernate: 
    /* select
        m 
    from
        Member m */ select
            member0_.id as id1_0_,
            member0_.age as age2_0_,
            member0_.team_id as team_id4_0_,
            member0_.username as username3_0_ 
        from
            Member member0_
resultMany = []

 

query.getSingleResult() : 결과가 정확히 하나일때, 단일 객체를 반환한다.

 - 결과가 없으면 NoResultException 예외발생

 - 결과가 2개 이상이면 NonUniqueResultException 예외발생

Member resultOne = em.createQuery("select m from Member m", Member.class).getSingleResult();
System.out.println("resultOne = " + resultOne);
Hibernate: 
    /* select
        m 
    from
        Member m */ select
            member0_.id as id1_0_,
            member0_.age as age2_0_,
            member0_.team_id as team_id4_0_,
            member0_.username as username3_0_ 
        from
            Member member0_
javax.persistence.NoResultException: No entity found for query
	at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1555)
	at jpql.JpaMain.main(JpaMain.java:27)

파라미터 바인딩

 

파라미터를 바인딩하는 방법은 2가지가 있다.

첫번째로, 파라미터를 이름으로 받는 방법이다.

List resultList = em.createQuery("select m from Member m where m.username =:username")
        .setParameter("username", "member1")
        .getResultList();

 

두번째로, 순서로 받는 방법이다.

해당 방법은 순서가 변하면 오류가 생길 수 있어서 지양하는 방법이다.

List resultList = em.createQuery("select m from Member m where m.username =?1")
        .setParameter(1, "member1")
        .getResultList();

프로젝션 -> 스칼라 타입 프로젝션

여러값 조회할때 사용하는 방법 (new 명령어로 조회)

public class MemberDTO {

    private String username;
    private int age;

    public MemberDTO(String username, int age) {
        this.username = username;
        this.age = age;
    }

패키지 명을 모두 적어주어야한다.

List<MemberDTO> result = em.createQuery("select distinct new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
        .getResultList();

페이징

for (int i = 0; i < 100; i++) {
    Member member = new Member();
    member.setUsername("member" + i);
    member.setAge(10 + i);
    em.persist(member);
}

em.flush();
em.clear();

List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
        .setFirstResult(1)
        .setMaxResults(10)
        .getResultList();

System.out.println("result.size() = " + result.size());
for (Member member1 : result) {
    System.out.println("member1 = " + member1);
}

tx.commit();

내림차순으로 1부터 시작했으므로 98~89까지 출력된다.

또한, 방언을 교체할 수 있어서, MySQL 또는 Oracle 로 변경해서 실행하면 알맞은 쿼리를 확인할 수 있다.

Hibernate: 
    /* select
        m 
    from
        Member m 
    order by
        m.age desc */ select
            member0_.id as id1_0_,
            member0_.age as age2_0_,
            member0_.team_id as team_id4_0_,
            member0_.username as username3_0_ 
        from
            Member member0_ 
        order by
            member0_.age desc limit ? offset ?
result.size() = 10
member1 = Member{id=99, username='member98', age=108}
member1 = Member{id=98, username='member97', age=107}
member1 = Member{id=97, username='member96', age=106}
member1 = Member{id=96, username='member95', age=105}
member1 = Member{id=95, username='member94', age=104}
member1 = Member{id=94, username='member93', age=103}
member1 = Member{id=93, username='member92', age=102}
member1 = Member{id=92, username='member91', age=101}
member1 = Member{id=91, username='member90', age=100}
member1 = Member{id=90, username='member89', age=99}

조인

 

내부조인

em.createQuery("select m from Member m join m.team t");

외부조인

em.createQuery("select m from Member m left join m.team t");

 

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

 -> 일반 SQL 문과 크게 다르지 않다.

em.createQuery("select m from Member m join m.team t on t.name = 'A'");

EX) 회원의 이름과 팀의 이름이 같은 대상 외부 조인

SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name

 

 

 

++ 추가

 

복잡한 동적 쿼리는 QueryDSL 을 사용해서 해결한다.

JPAFactoryQuery query = new JPAQueryFactory(em);
 QMember m = QMember.member;
 List<Member> list =
 	query.selectFrom(m)
 		.where(m.age.gt(18))
 		.orderBy(m.name.desc())
 		.fetch();

- 문자가 아닌 자바 코드로 작성 가능

- 컴파일 시점에 문법 오류를 찾을 수 있다.

- 실무에서 자주 사용한다

728x90

'Spring > JPA' 카테고리의 다른 글

Querydsl 설정  (0) 2022.10.09
쿼리메소드 - 정렬처리  (0) 2022.10.09
즉시로딩 지연로딩  (0) 2022.09.27
JPA 프록시  (1) 2022.09.26
상속관계 매핑 ( @Inheritance , @DiscriminatorColumn )  (0) 2022.09.23

댓글