일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 이펙티브자바
- 앤드밀
- JUnit5
- 서오릉맛집
- 한우오마카세
- exceptionHandler
- 압구정로데오맛집
- 셀렉티
- 데이트코스
- 아일랜드리솜
- 녹는다녹아
- 경기족발
- 이속우화
- Java
- 서오릉
- 압구정곱떡
- 인생맛집
- 인생소고기집
- 청춘면가
- @ControllerAdvice
- 데이트
- 맛집
- 상암동
- EffectiveJava
- 토비의스프링
- @ExceptionHandler
- spring
- 고기김치
- 닭껍질만두
- 상암동맛집
- Today
- Total
Hyeonuk_.log
자바 ORM 표준 JPA 프로그래밍 읽다가 [10장 객체지향 쿼리 언어-1] 본문
JPQL (Java Persistence Query Language)
특징
- 객체지향 쿼리 언어다. 따라서 테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리한다.
- SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
- 결국 SQL로 변환된다.
SELECT 문
SELECT m FROM Member AS m where m.username = 'Hello'
엔티티와 속성은 대소문자 구분한다, Member는 클래스 명이 아니라 엔티티명이다. 별칭은 필수이다.
TypeQuery, Query
작성한 JPQL을 실행하려면 쿼리 객체를 만들어야 한다. 쿼리 객체는 TypeQuery와 Query가 있는데 반환할 타입을 명확하게 지정할 수 있으면 TypeQuery 객체를 사용하고, 반환 타입을 명확하게 지정할 수 없으면 Query 객체를 사용하면 된다.
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
// 반환할 타입이 지정되어 있는 경우
List<Member> resultList = query.getResultList();
for(Member member : resultList) {
System.out.println("member = " + member);
}
Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
// 반환할 타입이 명확하지 않는 경우, username은 String age는 Integer
List resultList = query.getResultList();
for(Object o : resultList) {
Object[] result = (Object[]) o; // 결과가 둘 이상이면 Object[] 반환, 하나면 Object
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);
}
query.getResultList() 를 사용할 때 만약 결과가 없으면 빈 컬렛견을 반환한다.
파라미터 바인딩
String usernameParam = "User1";
// 이름 기준 파라미터
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username = :username", Member.class);
query.setParameter("username", usernameParam);
List<Member> resultList = query.getResultList();
// 위치 기준 파라미터
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username
= ?1", Member.class).setParameter(1, usernameParam);
List<Member> resultList = query.getResultList();
프로젝션
SELECT절에서 조회할 대상을 지정하는 것을 프로젝션이라 하고 [SELECT (프로젝션 대상) FROM]으로 대상을 선택한다.
프로젝션 대상은 엔티티, 임베디드 타입, 스칼라 타입이 있다.
엔티티 프로젝션
SELECT m FROM Member m
SELECT m.team FROM Member m
// 조회한 엔티티는 영속성 컨텍스트에서 관리된다.
임베디드 타입 프로젝션
임베디드 타입은 조회 시작점이 될 수 없다는 제약이 있다. 임베디드 타입은 엔티티 타입이 아닌 값 타입이다. 따라서 직접 조회한 임베디드 타입은 영속성 컨텍스트에서 관리되지 않는다.
String query = "SELECT o.address FROM Order o"; // Order를 통해서 Address 접근
List<Address> addresses = em.createQuery(query, Address.class).getResultList();
select
order.city,
order.street,
order.zipcode
from
Orders order
스칼라 타입 프로젝션
숫자, 문자, 날짜와 같은 기본 데이터 타입들을 스칼라 타입이라 한다. 예를 들어 전체 회원의 이름을 조회하려면 다음처럼 쿼리하면 된다.
List<String> username = em.createQuery("SELECT username FROM Member m", String.class)
.getResultList();
Double orderAmountAvg = em.createQuery("SELECT AVG(o.orderAmount) FROM ORder o", Double.class)
.getResultList();
여러값 조회
Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
List resultList = query.getResultList();
Iterator iterator = resultList.iterator();
while(iterator.hasNext()) {
Object[] row = (Object[]) iterator.next();
String username = (String) row[0];
Integer age = (Integer) row[2];
}
Object[]로 좀 더 간결하게
List<Object[]> resultList = em.createQuery("SELECT m.username, m.age FROM Member m").getResultList();
for(Object[] row : resultList) {
String username = (String) row[0];
Integer age = (Integer) row[1];
}
스칼라 값 뿐만 아니라 엔티티로 조회
List<Object[]> resultList =
em.createQuery("SELECT o.member, o.product, o.orderAmount FROM Order o").getResultList();
for(Object[] row : resultList) {
Member member = (Member) row[0];
Product product = (Product) row[1];
int orderAmount = (Integer) row[2];
}
New 명령어 사용
List<Object> resultList =
em.createQuery("SELECT m.username, m.age FROM Member m").getResultList();
//객체변환 작업
List<UserDTO> userDTOs = new ArrayList<UserDTO>();
for(Object[] row : resultList) {
UserDTO userDTO = new UserDTO((String) row[0], (Integer)row[1]);
userDTOs.add(userDTO);
}
return userDTOs;
// NEW 명령어 사용하면
TypedQuery<UserDTO> query =
em.createQuery("SELECT new jpabook.jpql.UserDTO(m.username, m.age)
FROM Member m", UserDTO.class);
List<UserDTO> resultList = query.getResultList();
페이징 API
페이징 처리용 SQL을 작성하는 일은 지루하고 반복적이다. 더 큰 문제는 데이터베이스마다 페이징을 처리하는 SQL 문법이 다르다는 점이다. JPA는 페이징을 다음 두 API로 추상화했다.
- setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작한다)
- setMaxResult(int maxResult) : 조회할 데이터 수
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m ORDER BY m.username DESC",Member.class)
.setFirstResult(10)
.setMaxResult(20);
// 11번째부터 시작해서 총 20건의 데이터를 조회
query.getResultList();
'Dev_.log > Spring' 카테고리의 다른 글
토비 스프링 읽다가 트랜잭션 관련 기록 - 1 (0) | 2022.05.02 |
---|---|
토비의 스프링 읽다가 템플릿/콜백 기록 (0) | 2022.03.06 |
[Junit5] @BeforeAll를 사용하다...멘붕...(토비스프링 읽다가) (0) | 2022.03.06 |
[Spring] @Service 단위테스트 (0) | 2022.02.21 |
[Spring] RestTemplate 업무 사용할 때 알아야할 점, 주의할 점 (0) | 2022.02.19 |