일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 이것이 MySQL이다
- directed graphical model
- 2018
- Perceptron Convergence theorem
- 5397번
- 알고리즘대회
- MySQL
- vector미분
- 델타 rule
- 알고리즘
- 1차예선
- SCPC
- 인공지능
- 선형분류
- secant
- falsePosition
- 스터디
- graphical models
- 개발순서
- undirected graphical model
- CH01
- chapter01
- Fisher discriminant analysis
- Numerical optimization
- bisection
- 선형판별분석
- 로지스틱 회귀
- 근구하기
- 자바ORM표준JPA프로그래밍
- chapter02
- Today
- Total
computer_study
[JPA]10. 객체지향 쿼리 언어 본문
1. 객체지향 쿼리 소개
- 테이블이 아닌 객체를 대상으로 실행
- JPA가 지원하는 기능
- JPQL
- 엔티티 객체를 조회하는 객체지향 쿼리
- 문법은 SQL과 비슷하나 SQL보다 간결하다
- SQL을 추상화 해서 특정 데이터베이스 SQL에 의존하지 않는다.
- JPQL은 결국 SQL로 변환된다.
- Criteria 쿼리
- Criteria는 JPQL을 생성하는 빌더 클래스
- 프로그래밍 코드로 JPQL을 작성할 수 있다.
- 컴파일 시점에 오류를 발견할 수 있다.
- IDE를 사용하면 코드 자동완성을 지원한다.
- 동적 쿼리를 작성하기 편하다.
- 복잡하고 장황하여 불편하고, 코드가 한 눈에 들어오지 않는다는 단점이 있다.
- 네이티브 SQL
- SQL을 직접 사용할 수 있는 기능
- 특정 데이터베이스만 사용하는 기능이나 힌트들을 사용할 때 사용.
- 데이터베이스를 변경하면 네이티브 SQL도 수정해야 한다.
- QueryDSL
- JPQL빌더 역할을 한다.
- 코드 기반이면서 단순하고 사용하기 쉽다.
- 어노테이션 프로세서를 사용해서 쿼리 전용 클래스를 만들어야 한다.
- JDBC 직접 사용 (MyBatis같은 SQL매퍼 프레임워크 사용)
- JPA는 JDBC커넥션을 획득하는 API를 제공하지 않기에 JPA 구현체가 제공하는 방법을 사용해야 한다.
- JDBC나 마이바티스를 JPA와 함께 사용하면 영속성 컨텍스트를 적절한 시점에 강제로 플러시 해야한다. (JDBC나 마이바티스 사용 시 DB에 우회해서 접근하게 되는데, 이는 JPA가 인식할 수 없으므로 플러시를 통해 데이터베이스와 영속성 컨텍스트를 동기화)
- JPQL
2. JPQL
2.1 기본 문법과 쿼리 API
SELECT, UPDATE, DELETE문을 사용할 수 있고, 엔티티 저장 시 EntityManager.persist()메소드를 사용하므로 INSERT문은 없다.
- SELECT
- 엔티티와 속성은 대소문자를 구분(SELECT, FROM같은 JPQL키워드는 구분 x)
- 클래스 명은 엔티티 명으로 사용하는 것을 추천
- 별칭을 필수로 사용해야 한다 (Member AS m 아니면 Member m)
- TypeQuery, Query
- 작성한 JPQL을 실행하기 위한 쿼리 객체
- 반환 타입이 명확하다면 TypeQuery객체
- 명확하지 않다면 Query 객체
- 결과 조회
- 메소드 호출로 쿼리를 실행해서 결과를 조회할 수 있다.
- query.getResultList() : 결과를 예제로 반환. 없으면 빈 컬렉션 반환
- query.getSingleResult() : 결과가 정확히 하나일 때 사용
2.2 파라미터 바인딩
- 이름 기준 파라미터
- 파라미터를 이름으로 구분
- em.createQuery("SELECT m FROM Member m where m.username = :username", Member.class) 파라미터 정의
- query.setParameter("username", usernameParam) 으로 파라미터 바인딩
- 위치 기준 파라미터
- em.createQuery("SELECT m FROM Member m where m.username = ?1", Member.class).setParameter(1, usernameParam)
- 위치 기준보단 이름 기준을 사용하는 것이 더 명확
2.3 프로젝션
SELECT절에 조회할 대상을 지정하는 것 (SELECT [프로젝션 대상] FROM)
- 엔티티 프로젝션
- SELECT m FROM Member m 같이 원하는 객체를 바로 조회
- 컬럼을 나열해서 조회하지 않아도 된다.
- 조회한 엔티티는 영속성 컨텍스트에서 관리
- 임베디드 타입 프로젝션
- 엔티티와 거의 비슷하게 사용된다.
- 조회의 시작이 될 수 없기에 엔티티를 우선 조회 후 임베디드 타입을 조회할 수 있다.
- Order가 엔티티 Address(city, street, zipcode)가 임베디드 타입이라면 order.city 이런식으로 조회해야
- 직접 조회한 임베디드 타입은 영속성 컨텍스트에서 관리되지 않는다.
- 스칼라 타입 프로젝션
- 숫자, 문자, 날짜와 같은 기본 데이터 타입들
- 여러 값 조회
- 필요한 데이터들만 선택해서 조회할 때 Query를 사용해서 가능하다.
- em.createQuery("SELECT m.username, m.age FROM Member m")
- NEW 명령어
- 반환받을 클래스를 지정해서 생성자에 JPQL 조회 결과를 넘겨줄 수 있다. (객체 변환 작업을 줄일 수 있다.)
- em.createQuery("SELECT new jpabook.jpql.UserDTO(m.username, m.age)
FROM Member m", UserDTO.class) - 주의사항
- 패키지 명을 포함한 전체 클래스 명을 입력해야 한다.
- 순서와 타입이 일치하는 생성자가 필요하다.
2.4 페이징 API
페이징을 두 API로 추상화해서, 데이터베이스마다 다른 페이징 처리를 같은 API들로 처리할 수 있도록 하였다(Dialect 사용)
- setFirstResult(int startPosition): 조회 시작 위치(0부터 시작)
- setMaxResults(int maxResult): 조회할 데이터 수
2.5 집합과 정렬
통계 정보를 구할 때 사용. SQL과 같음
- 집합함수
- COUNT
- MAX, MIN
- AVG
- SUM
- GROUP BY, HAVING
- ORDER BY
2.6 JPQL 조인
SQL조인과 같은 기능. 문법만 살짝 다르다
조인은 스킵
2.8 경로 표현식
"." 을 찍어서 객체 그래프를 탐색할 수 있다.
- 상태 필드
- 단순히 값을 저장하기 위한 필드
- 경로 탐색의 끝으로 더는 탐색할 수 없다.
- 연관 필드
- 연관관계를 위한 필드
- 단일 값 연관 필드
- 묵시적으로 내부 조인이 일어나고 단일 값 연관 경로는 계속 탐색할 수 있다.
- 컬렉션 값 연관 필드
- 묵시적으로 내부 조인이 일어나고 더는 탐색할 수 없다.
- FROM절에서 조인을 통해 별칭을 얻으면 별칭으로 탐색할 수 있다.
- 주의사항
- 항상 내부 조인이다.
- 묵시적 조인이 사용되므로 sql from절에도 영향을 준다. (명시적 조인을 사용하는 것이 좋다.)
2.9 서브쿼리
WHERE, HAVING절에서만 사용할 수 있고 SELECT, FROM절에서는 사용할 수 없다.
2.10 조건식
2.11 다형성 쿼리
JPQL로 부모 엔티티를 조회하면 그 자식 엔티티도 함께 조회된다.
2.12 사용자 정의 함수 호출(JPA 2.1)
JPA 2.1 부턴 사용자 정의 함수를 호출할 수 있다.
방언클래스를 상속해서 구현 후 방언을 등록해두어 사용이 가능하다.
2.14 엔티티 직접 사용
JPQL에서 엔티티를 직접 사용하면 SQL로 변환될 때 엔티티의 기본 키를 사용하게 된다.
select count (m.id) from Member m
select count (m) from Member m
// 실행되는 SQL은 두 개가 같다
2.15 Named쿼리: 정적 쿼리
미리 정의한 쿼리에 이름을 부여해서 필요할 때 사용할 수 있게 하는 것이 정적 쿼리
cf) 동적쿼리: em.createQuery("select ..")처럼 JPQL을 문자로 완성해서 직접 넘기는 것
특징
- 애플리케이션 로딩 시점에 JPQL 문법을 체크하고 미리 파싱한다
- 오류를 빨리 확인할 수 있다.
- 사용하는 시점에는 파싱된 결과를 재사용하므로 성능상 이점이 있다
- 변하지 않는 정적 SQL이 생성되므로 DB조회 성능에 도움이 된다.
@NamedQuery 어노테이션을 사용하거나 XML문서에 작성할 수 있고 우선권은 XML이 갖는다.
3. Criteria
3.1 Criteria 기초
- JPQL을 자바 코드로 작성하도록 도와주는 빌더 클래스 API
- 코드로 JPQL을 작성한다
- 장점
- 문법 오류를 컴파일 단계에서 잡을 수 있다
- 문자 기반의 JPQL보다 동적 쿼리를 안전하게 생성할 수 있다
- 단점
- 코드가 복잡하고 장황해서 직관적으로 이해가 안된다.
CriteriaBuilder cb = em.CriteriaBuilder(); // 빌더 생성
CriteriaQuery<Member> cg = cb.createQuery(Member.class); // 생성, 반환 타입 지정
Root<Member> m = cq.from(Member.class); // from절 생성, 조회의 시작으로 Root
cq.select(m); // select절 생성
TypedQuery<Member> query = em.createQuery(cq);
List<Member> members = query.getResultList();
3.2 Criteria 쿼리 생성
CriteriaBuilder.createQuery() 메서드를 통해 쿼리 생성 가능
3.3 조회
// 한 건 조회
cq.select(m);
// 여러 건 조회
cq.multiselect(m.get("username"), m.get("age"));
cq.select( cb.array(m.get("username"), m.get("age")) );
// distinct
cq.multiselect(m.get("username"), m.get("age")).distinct(true);
3.4 집합
cq.multiselect(m.get("team").get("name"), maxAge, minAge)
.groupBy(m.get("team").get("name")) // groupby
.having(cb.gt(minAge, 10); // having
3.5 정렬
cq.select(m).where(ageGt).orderBy(cb.desc(m.get("age")));
3.6 조인
3.7 서브쿼리
메인 쿼리와 서브 쿼리가 관련이 있다면 메인 쿼리에서 사용한 별칭을 얻어서 쿼리해야 한다.
Root<Member> subM = subQuery.correlate(m) 이런 식으로 메인 쿼리의 별칭을 가져올 수 있다.
3.8 IN
in 사용 가능
3.9 CASE
selectCase(), whne(), otherwise() 사용 가능
3.10 파라미터 정의
파라미터 정의 후 바인딩 가능
cb.parameter(타입, 파라미터 이름) -> setParameter(파라미터 이름, 파라미터)
3.11 네이티브 함수 호출
cb.function()메소드 사용
3.12 동적 쿼리
- 다양한 검색 조건에 따라 실행 시점에 쿼리를 생성하는 것
- 문자 기반인 JPQL보다 코드 기반인 Criteria로 작성하는 것이 더 편리(공백이나 where, and의 위치로 인해 에러발생이 없어서)
3.13 함수 정리
p.424
3.14 Criteria 메타 모델 API
m.get("age")대신 m.get(Member_.age)처럼 문자 기반에서 정적 코드 기반으로 변경할 수 있다.
하이버네이트의 코드 생성기를 통해 메타 모델 클래스를 자동으로 만들 수 있다.
4. QueryDSL
- 쿼리를 문자가 아닌 코드로 작성해도 쉽고 간결하며 그 모양도 쿼리와 비슷하게 개발 할 수 있도록
- JPA, JDO, JDBC, Lucene, Hibernate Search, 몽고DB, 자바 컬렉션 등 다양하게 지원
JPAQuery query = new JPAQuery(em);
QItem item = QItem.item;
List<item> list = query.from(item).where(item.name.eq("좋은상품").and ..)
.list(item); // 조회 할 프로젝션 지정
// 결과 호출 시
uniqueResult()
singleResult()
list()
페이징, 정렬, 그룹, 조인, 서브쿼리 모두 사용 가능
QueryDSL은 코드로 쿼리를 작성할 수 있으면서 동적쿼리가 복잡하지 않기에 좋다.
5. 네이티브 SQL
- JPQL을 사용할 수 없을 때 SQL을 직접 사용하도록 하는 기능
- 엔티티를 조회할 수 있고 JPA가 지원하는 영속성 컨텍스트의 기능을 그대로 사용할 수 있다.
- 네이티브 SQL로 SQL만 직접 사용할 뿐 나머지는 JPQL을 사용할 때와 같다.
String sql = "SELECT ID, AGE" +
"FROM MEMBER WHERE AGE > ?";
Query nativeQuery = em.createNativeQuery(sql, Member.class).setParameter(1,20);
List<Member> resultList = nativeQuery.getResultList();
// 결과 타입정의 없이 단순한 값으로 조회 시
createNativeQuery(String sqlString)
// 엔티티와 스칼라를 함께 조회하는 등, 매핑이 복잡해졌을 때
createNativeQuery(String sqlString, String resultSetMapping)
- 결과 매핑 애노테이션 정리 - p.449
- JPQL처럼 Named 네이티브 SQL을 사용해서 정적 SQL을 작성할 수 있다.
- Query, TypeQuery를 반환한다.
- 관리하기가 쉽지 않고, 특정 데이터베이스에 종속적인 쿼리가 증가해 이식성이 떨어지는 단점이 있지만 사용을 안할 수 없어, JPQL -> JPA 구현체 기능 -> 네이티브 SQL -> MyBatis, JdbcTemplate등을 사용하자
6. 객체지향 쿼리 심화
- 벌크연산
- 여러 건을 한 번에 수정하거나 삭제할 수 있다
- executeUpdate()메소드를 사용할 수 있다.
- 영속성 컨텍스트를 무시하고 DB에 직접 쿼리하므로 영속성 컨텍스트와 DB간 데이터 차이에 주의해야 한다.
- 가능하면 벌크 연산을 가장 먼저 수행하고 영속성 컨텍스트를 초기화 하는 것도 필요하다
- 영속성 컨텍스트와 JPQL
- 영속성 컨텍스트에 엔티티가 있으면 DB에서 조회한 결과를 버리고 영속성 컨텍스트에 있던 엔티티를 반환한다.
- 영속성 컨텍스트는 영속 상태인 엔티티의 동일성을 보장하기에 기존 엔티티를 그대로 두고, 새로 검색한 엔티티를 버린다.
- JPQL과 플러시 모드
- JPQL은 영속성 컨텍스트에 있는 데이터를 고려하지 않고 DB에서 데이터를 조회하므로 JPQL실행 전에 영속성 컨텍스트의 내용을 데이터베이스에 반영해야 한다.
- 일반적으로 플러시 모드의 기본값이 AUTO라 신경 쓸 것이 없지만 COMMIT모드 사용 시에는 em.flush()로 수동으로 처리하거나 setFlushMode()로 특정 쿼리만 플러시 모드를 AUTO로 해주면 된다.
- COMMIT모드(트랜젝션 커밋 시에만 플러시. 쿼리 실행때는 플러시 하지 않는다) 를 사용하는 이유는 플러시 횟수를 줄여 성능을 최적화 하기 위함
'스터디 > 자바 ORM표준 JPA 프로그래밍' 카테고리의 다른 글
[JPA] 13. 웹 애플리케이션과 영속성 관리 (0) | 2022.10.04 |
---|---|
[JPA] 12. 스프링 데이터 JPA (0) | 2022.09.26 |
[JPA] 08. 프록시와 연관관계 관리 (0) | 2022.08.22 |
[JPA] 07. 고급 매핑 (0) | 2022.08.08 |
[JPA] 06. 다양한 연관관계 매핑 (0) | 2022.07.31 |