일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- LIST
- 프로젝트생성
- calendar
- 벌크연산
- paging
- JQuery
- fullcalendar
- jQuery값전송
- Hibernate
- jscalendar
- Generic
- jQuery값전달
- springflow
- fetchjoin
- joinfetch
- 스프링데이터흐름
- 대량쿼리
- 페치조인
- 자바서블릿
- 제너릭
- 제네릭
- 페이징
- values()
- JPQL
- jQueryUI
- namedQuery
- JPA
- javaservlet
- 엔티티직접사용
- javascriptcalendar
- Today
- Total
가자공부하러!
Hibernate(8) - 객체지향 쿼리 언어(JPQL, QueryDSL 등) 본문
1. JPQL(Java Persistence Query Language)
1.1. 등장배경
- JPA를 사용하면 엔티티 객체를 중심으로 개발을 하게됨
- 검색할 때 마다 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
- 필요한 데이터만 불러오려면 검색 조건이 포함된 SQL이 필요
1.2. 특징
- SQL을 추상화한 쿼리 언어이며, SQL이 지원하는 키워드는 모두 지원
- JPQL은 엔티티 객체를 대상으로 쿼리 수행(SQL은 DB 테이블 대상)
- JPQL은 SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
- JPQL은 결국 SQL로 변환된다.
1.3. 사용방법과 예제
- 기초 사용법
> 엔티티와 속성은 대소문자 구분, JPQL 키워드(select, FROM, ...)는 대소문자 구분하지 않음
> 테이블 이름을 사용하지 않고 엔티티 이름을 사용함
> 별칭(alias) 필수 (select m from Member as m)
//Member는 테이블이 아니고 엔티티, 2번 째 매개변수로 엔티티 명시
List<Member> resultList = em.createQuery(
"select m from Member m where m.username like '%kim%'", Member.class)
.getResultList();
- 반환타입에 따른 쿼리 저장 타입
> 반환타입이 명확한 경우
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
> 반환타입이 명확하지 않은 경우
Query query = em.createQuery("select m.username, m.age from Member m");
- 결과 조회 API
> 결과가 하나 이상일 때 사용 : query.getResultList();
- 결과가 없으면 빈 리스트 반환
> 결과가 정확하게 하나일 때 사용 : query.getSingleResult();
- 결과가 없으면 NoResultException
- 결과가 둘 이상이면 NonUniqueResultException
- 파라미터 바인딩
// 콜롱으로 지정(:username)
String inputName = "memberA";
TypedQuery<Member> query = em.createQuery("select m from Member m where m.username = :username", Member.class);
query.setParameter("username", inputName);
//메소드 체이닝 방식
Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", inputName)
.getSingleResult();
- 프로젝션
> 특징
- SELECT 절에 조회할 대상을 지정하는 것
> 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(문자, 숫자 등 기본 데이터 타입)
> 엔티티 프로젝션 : 조회할 대상을 엔티티로 지정
- 엔티티 프로젝션을 통해 조회된 엔티티들은 영속성 컨텍스트의 관리를 받음
> 임베디드 타입 프로젝션 : 조회할 대상을 임베디드 타입으로 지정
- 임베디드 타입 독자적으로 가져올 수는 없고 종속된 엔티티로 부터 받아와야 함
// Address는 임베디드타입이며, Order에 종속됨
em.createQuery("select o.address from Order o", Address.class).getResultList();
//아래 쿼리처럼 사용할 수는 없음
em.createQuery("select a from Address a", Address.class).getResultList();
> 스칼라 타입 프로젝션 : 조회할 대상을 기본 데이터 타입으로 지정
- 여러 값 조회 방법
> Query 타입 조회 : 하나의 데이터 행을 Object[]로 리턴함
> Object[] 조회 : 위 특성을 이용해서 ResultList의 제너릭을 Object[]로 지정
> new 명령어로 조회
- 엔티티가 아니면서 생성자를 갖는 새로운 클래스(DTO)를 만들어서 리턴
- 순서와 타입이 일치하는 생성자 필요
//MemberDTO는 엔티티가 아닌 클래스이다. new 연산자와 함께 패키지 이름을 명시해주어야 한다.
List<MemberDTO> resultList = em.createQuery(
"select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class);
- 페이징 API
> JPA는 페이징을 다음 두 API로 추상화
- setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
- setMaxResults(int maxResult) : 조회할 데이터 수
> 예제
//age 내림차순 0번 째 값 부터 10개 가져오는 방식
em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
- 세부 쿼리는 DB에 따라 다르게 설정됨(방언)
> 예) Oracle DB는 1부터 시작하고 MySql은 0부터 시작하는 차이가 있는데, JPA는 설정된 방언에 맞게 알아서 쿼리를 생성해 준다
- 조인
> 내부 조인 : select m from Member m [INNER] JOIN m.team t
> 외부 조인 : select m from Member m LEFT [OUTER] JOIN m.team t
- 조인 대상 필터링
> select m, t FROM Member m LEFT JOIN m.team t on t.name ='A'
> 세타 조인(연관관계가 없는 엔티티 간의 조인) : select count(m) from Member m, Team t where m.username = t.name
> ON 절
- 연관관계 없는 엔티티 외부 조인 가능
- 예) select m, t FROM Member m LEFT JOIN Team t on m.username = t.name
> 실제SQL : SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name
- 서브쿼리
> 특징과 제약
- JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
- HIBERNATE에서만 SELECT 절 가능
- FROM절에서의 서브쿼리는 불가능 -> 정 안되겠다 싶은 것들은 app쪽에서 가공
> 지원 함수
- [NOT] EXISTS (subquery) :
> 팀A 소속인 회원
- select m from Member m where exists (select t from m.team t where t.name ='팀A')
> 전체 상품 각각의 재고보다 주문량이 많은 주문들
- select o from Order o where o.orderAmount > ALL(select p.stockAmount from Product p)
> 어떤 팀이든 팀에 소속된 회원
- select m from Member m where m.team = ANY(select t from Team t)
- [NOT] IN (subquery) : 하나라도 같은 것이 있으면 참
> 예) 나이가 평균보다 많은 회원
- select m from Member m where m.age > (select avg(m2.age) from Member m2)
> 예) 한 건이라도 주문한 고객
- select m from Member m where (select count(o) from Order o where m = o.member) > 0
1.4. 타입 표현
> 문자 : 'HELLO', 'She''s'(작은따옴표 사용)
> 숫자 : 10L(Long), 10D(Double), 10F(Float)
> BOOLEAN : TRUE, FALSE
> ENUM : jpabook.MemberType.Admin(패키지명 까지 포함)
> 엔티티 타입 : TYPE(m) = Member
- select i from Item i where type(i) = Book
- 엔티티의 타입을 체크해줄 수 있다.
1.5. 조건식
> 기본 CASE 식 - QueryDSL에서 더 쉽게 가능
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
> 단순 CASE 식 - 기본 CASE식에서 조건만 ==으로 변경
- (... when m.age <= 10 ...) -> (... when 10 ...)
> COALESCE - 하나 씩 조회해서 null이면 대체값 사용(nvl과 유사)
- select coalesce(m.username, '이름 없는 회원') from Member m
> NULLIF - 두 값이 같으면 null, 다르면 첫 번째 값 반환
- select NULLIF(m.username, '관리자') from Member m -> 관리자는 null 리턴
1.6. 기본 함수
> JPQL 표준 함수 - 모든 DB 가능
- CONCAT : select concat('a', 'b') from Member m
- SUBSTRING : select substring(m.username, 2, 3) from Member m
- TRIM
- LOWER, UPPER
- LENGTH
- LOCATE : select locate('de', 'abcdefg') from Member m
- ABS, SQRT, MOD
- SIZE, INDEX(JPA 전용) : select size(t.members) From Team t
> 사용자 정의 함수
- 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.
package dialect;
/** 방언 클래스를 상속받은 클래스 */
public class MyH2Dialect extends H2Dialect {
public MyH2Dialect (){
registerFunction("함수이름", new StandardSQLFunction("함수이름", StandardBasicTypes.STRING));
}
}
<!-- persistence.xml -->
<persistence ... > <persistence-unit name="이름"> <properties>
...
<!-- 방언 클래스를 상속받은 클래스를 value로 설정 -->
<property name="hibernate.dialect" value="dialect.MyH2Dialect"/>
...
</properties> </persistence-unit> </persistence>
> DB종속 함수
- DBDialect 클래스에서 확인 가능
1.7. 한계
- qlString은 문자열이기 때문에 동적쿼리를 만들기 어렵다.
> 파라미터 바인딩으로 해결 가능
2. Criteria
2.1. 용도와 특징
- 용도 : 동적 쿼리를 명확하게 만들고자 할 때
- 장점 : 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
- 단점 : SQL스럽지 않고 알아보기 쉽지 않음 -> 그래서 잘 안씀
- Criteria 대신에 QueryDSL 사용 권장
3. QueryDSL
3.1. 특징
- 세팅이 복잡함
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
- 컴파일 시점에 문법 오류를 찾을 수 있음
- 동적 쿼리를 만들기 쉬움
- JPQL과 거의 1:1
4. 네이티브 SQL
4.1. 특징
- JPQL만으로 해결할 수 없는 특정 DB에 의존적인 기능을 사용하고자 할 때 활용
- em.createNativeQuery("select * from MEMBER");
'공부 > Java' 카테고리의 다른 글
Hibernate(10) - 응용 문법 ② (엔티티 직접 사용, named 쿼리, 벌크연산) (0) | 2019.11.26 |
---|---|
Hibernate(9) - 응용 문법 ① (경로 표현식, 페치 조인, 다형성 쿼리) (0) | 2019.11.26 |
Hibernate(7) - 값 타입 (0) | 2019.11.22 |
Hibernate(6) - 프록시, 즉시/지연 로딩, 연관관계 관리 (0) | 2019.11.22 |
Hibernate(5) - 연관관계 매핑 (0) | 2019.11.20 |