[JPA] 자바 ORM 표준 JPA 프로그래밍(기본편) 7 - JPQL 소개

유승선 ·2022년 11월 18일
1
post-thumbnail

JPQL 소개

기존에 EntityManager을 활용해서 .find() 메서드같이 엔티티를 찾아오는 방식은 JPA 에서 제공 해주고 있다. 그런데 좀 더 디테일한 엔티티를 찾고 싶을때는 (예: 나이가 18살 이상인 회원 검색) 단순한 .find() 보다 더 정밀화 해야함으로 JPQL 이란것을 사용할 수 있다.

JPA 를 사용하는것은 객체 지향적인 코드를 설계하면서 개발자로서 DB 테이블을 최대한 신경 안쓰기 위해서다. 이런 객체 중심적인 설계 덕분에 JPQL 은 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색 한다는 특징이 있다.

한마디로 객체 지향 SQL 로 요약할 수 있다.

JPQL 과 SQL의 차이점을 확인 할 수 있다. jpql을 저렇게 작성하게 되면 하이버네이트 구현체에서 RDBMS를 검색하는 일반 테이블 SQL 형식으로 전환되어서 간단하게 조회할 수 있다. 잘 외워둘것은 createQuery({jpql query},{return type in class})다.

.getResultList() 는 뒤에서 또 설명하겠지만 리스트 형식으로 받는 특징이 있다.

크게 의미는 없지만 객체를 사용하는 JPA 모델과 DB로 전환된 모델의 비교다.

JPQL 문법

프로그래머스 같은 곳에서 SQL 문제를 풀었듯이 사실 JPQL 을 사용할때도 굉장히 유사하다. 조금만 더 생각해보면 객체 지향 SQL 이라는 특징이 있기때문에 SQL 사용도 계속 연습해봐야겠다.

문법의 특징을 요약했다. 여기서 내가 개인프로젝트를 하면서 놓쳤던 부분이 있는데 별칭은 필수다. 잊지말자!

TypeQuery, Query

처음 강의를 들었을때 잘 이해 못했던 컨셉중 하나여서 몇번 찾아본 기억이 난다. Spring Data를 사용하고 있지 않는 지금으로서 createQuery() 메서드를 사용하고 JPQL 을 작성하게 되면 두번쨰 칸에 반환하는 타입을 적어야 한다.

TypedQuery는 만약 반환하는 타입이 명확하면은 사용하면되고

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

그렇다면 반환 타입은 어떤걸 말하는 걸까?

"SELECT m FROM MEMBER m" JPQL 같은 경우 멤버를 리턴하는게 너무 확실하다. 그렇기때문에 Member.class 타입을 명확히 제시해주었다.

"SELECT m.username FROM MEMBER m" JPQL 같은 경우 멤버가 아니고 멤버의 이름을 리턴하려고 한다. 멤버의 이름은 문자열이라는것이 확신하기 때문에 String.class 를 적어주면서 TypedQuery를 완성할 수 있다.

다만,

"SELECT m.username, m.age from Member m" 같은 경우는 두가지의 정보를 리턴하려고 하고 정확한 타입을 주기 힘들다. 그렇기 때문에 이때는 TypedQuery를 사용하지 못한다.

결과 조회 API

getResultList() 와 .getSingleResult() 가 있다. 한참 프로젝트를 진행할떄 .getResultList()를 사용하면서 하나의 값만 얻을 수 있는 매서드를 찾던중 .getSingleResult()를 발견했다.

그런데 순수 JPA 는 .getSingleResult()에 두가지 에러가 생기는데 첫번째는 두개이상의 결과가 나올때와 결과가 없을때 나오는 에러다.

.getResultList() 는 결과가 없으면 빈 리스트를 제공해주지만 .getSingleResult() 는 에러가 나오기때문에 나중에 스프링 JPA 에서는 이것을 NULL 값으로 처리해준다고 한다.

파라미터 바인딩

만약 특정 값으로 검색을 하고 싶다면은 setParameter을 이용해서 검색할 수 있다. 그리고 보통은 체인으로 연계해서 JPQL 을 작성한다. .setParameter.getResultList() 등등

프로젝션

프로젝션에는 여러가지 명칭이 존재한다. 그리고 조금 흥미롭게 봤던거는 스칼라 타입 프로젝션이다.

앞서 설명했듯이, "SELECT m.username, m.address.." 이런 쿼리들은 정확한 타입으로 조회가 안되고 결과를 받을 수 있는 방법을 생각해야한다. 그리고 여러가지 방법이 존재하지만 제일 괜찮은 방법은 new 명령어로 조회하는 방법이라고 했다.

마치 DTO를 생성하듯이 값을 가지고 올 클래스를 만들어서 JPQL의 타입에 포함 하는것이다!

위와 같은 예시가 존재한다. MemberDTO 자체를 클래스로 넣고 ResultList 를 가지고 온다음 MemberDTO 에 Getter 만 있으면 쉽게 결과를 가지고 올 수 있다.

페이징 API

페이징이란 단순하게 SELECT 쿼리를 여러개 날리는기 아니고 아예 페이지로 SELECT 문을 가지고 올 수 있는 쿼리 구현이다. 원래 MySQL도 그렇고 Oracle 도 그렇고 페이징 쿼리를 구현하는게 서브쿼리도 사용하면서 정말 복잡한데 JPA 에서는 진짜 쉽게 구현 가능하게 만들어줬다.

아직 페이징 쿼리를 사용하는 일이 없던 나로서 좀 신기하게 보이기도 했었고 나중에 꼭 사용해보고 싶다.

조인

조인 메커니즘과 관련해서 기본적인 정보 외에도 JPQL 에서는 내부 조인을 디폴트로 사용한다고 한다. 그렇기 때문에 예상치 못한 상황에서 내부조인 쿼리가 날라가서 특히 조심하라고 강조했다.

만약에 내가 조인을 안했는데도 조인 쿼리가 날라간다면은 최대한 작성하는 JPQL도 조인문으로 만들어줘야지 코드가 일관성 있고 깔끔하게 만들어진다고도 얘기한다.

조인 대상 필터링으로 위와 같이 사용할 수 있다. 조인을 해도 팀 이름이 A인 팀만 조인하는 방법.

이 방법은 일반적인 JOIN 쿼리를 사용할때 적는 문법과 굉장히 유사하다고 생각했다.

서브 쿼리

SQL 쿼리 작성에서 가장 어려운 서브 쿼리 작성법이다. 조금 복잡한 부분도 있지만 그래도 프로그래머스 같은 웹사이트에서 어느정도 연습의 시간이 필요하다고 생각한다. 문법도 그렇고 대부분 일반 SQL 을 사용하는것과 유사하다.

다만, 그냥 일반적으로 IN 만 사용하는것보다 강의에서 보여준 서브 쿼리 지원 함수는 여러가지가 존재한다.

가끔 헷갈릴때 참고해도 좋은 자료인거 같다.

JPQ 에서 사용되는 서브 쿼리에는 한계가 존재하는데 그것은

FROM 절의 서브 쿼리는 현재 JPQL 에서 불가능하다

즉, 이런 경우에는 조인으로 최대한 풀어보는게 답이다.

조건식 - CASE 식

CASE식을 배운게 프로그래머스 레벨 2-3 DB 문제같다. 그래도 굉장히 유사해서 그냥 보면 어떤 느낌인지 바로 확인 가능하다.

다른 형태의 조건식이고 조금 더 심화된 JPQL 사용법은 다음 포스트에서 다루도록 하겠다.

profile
성장하는 사람

2개의 댓글

comment-user-thumbnail
2022년 11월 21일

첫 출근 축하드립니당 하핫 무사히 하루 잘 마치시길 기원합니다!

1개의 답글