파게로그
JPA와 객체지향 쿼리 본문
🎈 JPA의 다양한 쿼리 방법
▪ JPQL
▪ JPA Criteria
▪ QueryDSL
▪ Native SQL
▪ JDBC API의 직접 사용, MyBatis, SpringJdbcTemplate과 함께 사용
JPQL(Java Persistence Query Language)
"database-independent query language that operates on the logical entity model as opposed to the physcial data model"
가장 단순한 조회 방법
- EntityManager.find()
- 객체 그래프 탐색(a.getB().getC())
JPA를 사용하면 엔티티 객체를 중심으로 개발
그러나, 문제는 검색 쿼리...
검색을 할 때에도 테이블이 아닌 엔티티 객체를 대상으로 검색
모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능함
애플리케이션이 필요한 데이터만 DB에서 불러오려면, 결국 검색 조건이 포함된 SQL이 필요함
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공함
SQL과 문법 유사. SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
JPQL은 엔티티 객체를 대상으로 쿼리
SQL은 데이터베이스 테이블을 대상으로 쿼리
테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리
SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않음
JPQL을 한 마디로 정의하면, 객체 지향 SQL
(SELECT e FROM Employee e)에서 SELECT문은 오직 Employee alias e만 나열하는데, 이는 쿼리의 result type이 Employee 객체임을 의미하며, 곧 이 문장을 실행하면 0개 또는 그 이상의 Employee 인스턴스의 리스트가 반환될 것임을 나타낸다.
FROM절에서 나열하지 않은 객체 또한 조회할 수 있다. SELECT e.department FROM Employee e에서, employee는 department라고 명명된 부서와 다대일 관계를 맺으며, 곧 쿼리의 result type은 Department 엔티티인 것이다.
// 검색
String jpql = "SELECT m FROM member m WHERE m.name LIKE '%HELLO%'";
List<Member> result = em.createQuery(jpql, Member.class).getResultList();
JPQL 문법
- select m from Member m where m.age > 18 // FROM절에 들어가는 것은 객체!
- 엔티티 이름을 사용. 테이블 이름이 아님.
- 별칭은 필수(m)
- 엔티티와 속성은 대소문자 구분O(Member, username, ...)
- JPQL 키워드는 대소문자 구분X(SELECT, FROM, WHERE, ...)
결과 조회 API
- query.getResultList(): 결과가 하나 이상. 리스트 반환
- query.getSingleResult(): 결과가 정확히 하나. 단일 객체 반환 (결과가 하나가 아니면 예외 발생)
파라미터 바인딩 - 이름 기준, 위치 기준 (웬만하면 이름 기준으로 바인딩하기)
SELECT m FROM Member m where m.username=:username
query.setParameter("username", usernameParam);
SELECT m FROM Member m where m.username=?1
query.setParameter(1, usernameParam);
프로젝션
SELECT m FROM Member m : 엔티티 프로젝션
SELECT m.team FROM Member m : 엔티티 프로젝션
SELECT username, age FROM Member m (hibernate에서는 m. 생략 지원): 단순 값 프로젝션
new 명령어 : 단순 값을 DTO로 바로 조회
SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
DISTINCT : 중복 제거
페이징 API
JPA는 페이징을 다음 두 API로 추상화
setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
setMaxResults(int maxResult) : 조회할 데이터 수
예시)
String jpql = "select m from Member m order by m.name desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
.setFirstResult(10)
.setMaxResults(20)
.getResultList();
페이징 API: Oracle 방언
SELECT * FROM
( SELECT ROW_.*, ROWNUM ROWNUM_
FROM
( SELECT
M.ID AS ID,
M.AGE AS AGE,
M.TEAM_ID AS TEAM_ID,
M.NAME AS NAME
FROM MEMBER M
ORDER BY M.NAME
) ROW_
WHERE ROWNUM <= ?
)
WHERE ROWNUM_ > ?
집합과 정렬
1. select
COUNT(m), // 회원 수
SUM(m.age), // 나이 합
AVG(m.age), // 나이 평균
MAX(m.age), // 나이 최대
MIN(m.age) // 나이 최소
from Member m
2. GROUP BY, HAVING
3. ORDER BY
조인
내부 조인: SELECT m FROM Member m [INNER] JOIN m.team t
외부 조인: SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
세타 조인: SELECT count(m) from Member m, Team t where m.username = t.name
cf. Hibernate 5.1부터 세타 조인도 외부 조인 가능
페치 조인:
- 엔티티 객체 그래프를 한번에 조회하는 방법
- 별칭을 사용할 수 없다.
- 마치 eager를 걸었듯이... 이 화면을 렌더링할 때만큼은 Member와 Team을 한 번에 가져와야겠다 싶으면 페치 조인을 사용(최신에는 entity graph를 사용)
- JPQL: select m from Member m join fetch m.team
- SQL: SELECT M.*, T.* FROM MEMBER T INNER JOIN TEAM T ON M.TEAM_ID=T.ID
JPQL 기타
-서브 쿼리 지원
-EXISTS, IN
-BETWEEN, LIKE, IS NULL
JPQL 기본 함수
- CONCAT
- SUBSTRING
- TRIM
- LOWER, UPPER
- LENGTH
- LOCATE
- ABS, SQRT, MOD
- SIZE, INDEX(JPA 용도)
오타가 있다고 치면..
NamedQuery는 application loading 시점, 배포 전 한 번 띄워만봐도에 오류를 잡을 수 있음.
그렇지 않으면 사용자가 실제로 이 sql을 실행하는 명령을 수행해야 오류가 발생함
'콤퓨타 왕기초 > JPA' 카테고리의 다른 글
실습 4 매핑 및 JPQL (0) | 2021.06.02 |
---|---|
JPA 내부 구조 (0) | 2021.06.02 |
연관관계 매핑 (0) | 2021.05.28 |
실습 3 (Mapping annotations) (0) | 2021.05.27 |
[실습] Mapping to DDL (0) | 2021.05.27 |