[JPA] JPQL (1)
JPA를 사용하면 기본적인 SQL은 알아서 작성되기 때문에 개발할 때 매우 편하다.
하지만 복잡한 SQL은 개발자가 직접 작성해야 하는데, sql을 작성하는 기술 중 하나가 JPQL이다.
JPA는 엔티티 객체를 중심으로 개발하기 때문에 검색 시에도 테이블 대신 엔티티 객체를 대상으로 검색할 수 있어야 한다.
데이터베이스에 있는 모든 데이터를 객체로 변환하는 방법은 너무 비효율적이다. 어쩔 수 없이 SQL이 사용된다.
JPA는 SQL을 추상화한 언어인 JPQL이라는 객체지향 쿼리 언어를 제공한다.
문법은 SQL과 유사하지만 JPQL은 객체를 대상으로 쿼리를 날리고 SQL은 데이터베이스 테이블을 대상으로 쿼리를 날린다는 차이점이 있다.
JPQL을 실행하면 해당 JPQL을 분석해서 적절한 SQL을 만들어 데이터베이스에 날라가는 방식으로 작동한다.
JPQL을 사용하면 데이터베이스에 쉽게 접근할 수 있지만, 사용자의 입력으로부터 쿼리를 생성하는 동적 쿼리를 다루기 힘들다.
이런 부분을 해결하기 위해 Criteria와 QueryDSL 기술이 도입됐다. (QueryDSL기술이 훨씬 많이 사용된다)
개발을 진행하다 보면 JPQL으로 해결할 수 없는 문제가 발생한다.
이런 경우 JPA는 SQL을 직접 사용하는 기능을 제공하니 문제 발생 시 사용하자. (JDBC기술과 MyBatis를 함께 사용할 수도 있다)
다른 데이터베이스 기술과 JPA를 함께 사용하는 경우 영속성 컨텍스트를 적절한 시점에 flush 해 줘야 한다.
JPQL은 Java Persistence Query Language의 약자로 SQL을 추상화하기 때문에 특정 데이터베이스의 SQL에 의존하지 않는다.
큰 흐름은
JPQL -> 사용하는 데이터베이스의 SQL로 변경 -> 데이터베이스에 쿼리 나감
으로 진행된다.
JPQL 속성에 대해 알아보자.
별칭
select m from Member as m where m.age > 18
엔티티와 속성 값은 대소문자를 구분해서 정의한대로 사용해야 하고 JPQL 키워드는 SQL처럼 대소문자를 구분하지 않아도 된다.
별칭 m은 필수로 넣어야 하고 as는 생략해도 괜찮다.
쿼리 저장하기
반환 타입이 명확한 경우 TypedQuery와 제네릭스를 함께 사용하고, query3처럼 반환 타입이 명확하지 않은 경우 Query를 사용해 쿼리를 저장할 수 있다.
위와 같이 쿼리를 지정한 후 getResult 등 쿼리 결과를 얻어오는 메서드를 사용한다.
getResultList() : 결과가 하나 이상인 경우 리스트를 반환하고 결과가 없으면 비어있는 리스트를 반환한다.
getSingleList() : 결과가 없거나 둘 이상이면 에러를 뱉고 하나면 해당 객체를 반환한다.
파라미터 바인딩
JPQL의 파라미터 바인딩은 : 를 사용해서 수행한다.
프로젝션
SELECT 절에 조회할 대상을 지정할 수 있다.
SELECT m FROM Member m : m은 Member의 별칭이다. Member 엔티티를 조회한다. (엔티티)
SELECT m.team FROM Member m : Member에 연관된 team을 조회한다. (엔티티)
SELECT m.address FROM Member m : address는 임베디드 타입이다. (임베디드)
SELECT m.username, m.age FROM Member m : username과 age는 Primitive 타입이다. (스칼라)
엔티티 프로젝션을 통해 얻은 결과는 영속성 컨텍스트로 관리되고, distinct를 붙여 중복 결과를 제거할 수 있다.
네 번째 예시에서 스칼라 프로젝션을 진행할 때 반환되는 값을 예측하기 힘든 경우 DTO을 따로 만들어서 처리하면 깔끔하다.
SELECT new DTO(m.username, m.age) FROM Member m
위와 같이 new 키워드를 사용하고, 이 때 순서와 타입이 일치하는 DTO 클래스의 생성자가 사용된다.
페이징
JPA에서는 두 가지 API로 페이징을 추상화한다.
setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작함에 주의)
setMaxResult(int maxResult) : 조회할 데이터 수
위와 같이 JPQL을 작성하면 설정된 데이터베이스에 맞춰서 SQL이 작성된다.
몇 번째부터 몇 개를 가져올지만 생각하면 된다.
JOIN
- 내부 조인
SELECT m FROM Member m [INNER] JOIN m.team t
INNER는 생략 가능하다.
- 외부 조인
SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
OUTER는 생략 가능하다.
- 세타 조인
select count(m) from Member m, Team t where m.username = t.name
아무런 연관관계가 없는 엔티티를 조인할 수 있다.
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
on 을 사용해 조인 대상을 필터링 할 수 있다. (team의 이름이 A 인 것만 조회)
연관관계가 없어도 on을 사용하면 조인할 수 있다.
서브쿼리
서브쿼리는 SQL에서의 개념과 유사하다.
select o from Order o
where o.orderAmount > ALL (select p.stockAmount from Product p)
EXISTS / ALL / ANY / SOME / IN 을 서브쿼리와 함께 사용해 진리값을 얻을 수 있다.
JPA는 WHERE / HAVING / SELECT 문법에서 서브쿼리를 지원하지만 FROM 문법에서는 서브쿼리를 사용할 수 없다. (SELECT는 하이버네이트에서 지원한다)
네이티브 SQL을 사용하거나 SQL을 두 번 날리는 방식으로 해결할 수 있고, 조인으로 풀 수 있으면 가장 좋다.
기본 함수
JPQL은 CONCAT, SUBSTRING, TRIM, LOWER, UPPER 등 프로그래밍 언어에서 많이 접했던 기본 함수를 제공한다.
이런건 역시 외우지 말고 필요할 때 찾아서 사용하도록 하자. (그러다 보면 손에 익는다)
따로 클래스를 만들어서 사용하는 데이터베이스를 상속받은 후 생성자에서 registerFunction 함수를 사용해 사용자 정의 함수를 등록할 수 있다.
'Spring > JPA' 카테고리의 다른 글
[JPA] OSIV (0) | 2022.12.31 |
---|---|
[JPA] JPQL (2) (0) | 2022.12.28 |
[JPA] 값 타입 (0) | 2022.12.26 |
[JPA] 영속성의 전이와 고아 객체 (0) | 2022.12.24 |
[JPA] 프록시와 연관관계 (0) | 2022.12.24 |
댓글
이 글 공유하기
다른 글
-
[JPA] OSIV
[JPA] OSIV
2022.12.31 -
[JPA] JPQL (2)
[JPA] JPQL (2)
2022.12.28 -
[JPA] 값 타입
[JPA] 값 타입
2022.12.26 -
[JPA] 영속성의 전이와 고아 객체
[JPA] 영속성의 전이와 고아 객체
2022.12.24