1. JPQL(객체지향 쿼리 언어)
- JPA를 사용하면 엔티티 객체를 중심으로 개발
- 문제는 검색 쿼리
- 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
- 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
- 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검 색 조건이 포함된 SQL이 필요
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 지원합니다. SQL 문법과 유사한 SELECT, FROM, WHERE, GROUP BY HAVING, JOIN을 지원합니다. 기존에 SQL을 사용하셨던 분들은 쉽게 배울 수 있습니다. JPQL은 엔티티 객체를 대상으로 쿼리를 합니다. SQL은 데이터베이스 테이블을 대상으로 쿼리를 날립니다.
String jpql = "select m from Member m where m.name like '%hello%'";
List<Member> result = em.createQuery(jpql, Member.class).getResultList();
테이블이아닌 객체를 대상으로 검색하는 객체지향 쿼리입니다. SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않습니다.
2. JPQL 문법
- select m from Member as m where m.age > 18
- 엔티티와 속성은 대소문자 구분O (Member, age)
- JPQL 키워드는 대소문자 구분X (SELECT, FROM, where)
- 엔티티 이름 사용, 테이블 이름이 아님(Member)
- 별칭은 필수(m) (as는 생략가능)
TypeQuery : 반환 타입이 명확할 때 사용
TypeQuery<Member> query = em.createQuery("SELECT m From Member m", Member.class);
Query : 반환 타입이 명확하지 않을때 사용
Query query = em.createQuery("SELECT m.username, m.age from Member m");
결과 조회 API
- query.getResultList(): 결과가 하나 이상일 때, 리스트 반환. nullpointerexception을 걱정하지 않아도 됩니다.
- 결과가 없으면 빈 리스트 반환
- query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환
- 결과가 없으면: javax.persistence.NoResultException
- 둘 이상이면: javax.persistence.NonUniqueResultException
- Spring Data Jpa는 결과가 없으면 NULL또는 옵셔널을 반환하도록 따로 처리를 하고 있습니다.
파라미터 바인딩 - 이름 기준, 위치 기준
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)
- SELECT 절에 조회할 대상을 지정하는 것
프로젝션 대상: 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 데이터 타 입) - SELECT m FROM Member m -> 엔티티 프로젝션
- SELECT m.team FROM Member m -> 엔티티 프로젝션
- SELECT m.address FROM Member m -> 임베디드 타입 프로젝션
- SELECT m.username, m.age FROM Member m -> 스칼라 타입 프로젝션
- DISTINCT로 중복 제거
프로젝션 여러값 조회
- SELECT m.username, m.age FROM Member m
- 1. Query 타입으로 조회
- 2. Object[] 타입으로 조회
- 3. new 명령어로 조회
- 단순 값을 DTO로 바로 조회 SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
- 패키지 명을 포함한 전체 클래스 명 입력
- 순서와 타입이 일치하는 생성자 필요
영속성 컨텍스트에서 다 관리가 되기 때문에 엔티티 프로젝션이라고합니다. m.team이라고 하면 result 결과를 Team으로 받아야합니다.
Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);
em.flush();
em.clear();
List<Team> result = em.createQuery("select m.team from Member m", Team.class).getResultList();
출력되는 쿼리를 보면 member와 team을 내부조인합니다. sql과 최대한 비슷하게 써야합니다 위에 처럼 쓰지말고 from절에서 join을 해줘서 가지고오는게 맞습니다.
List<Team> result = em.createQuery("select t from Member m join m.team t" Team.class).getResultList();
이렇게 해야 개발자가 join을 하는구나 예측이 됩니다.
임베디드 타입 프로젝션
em.createQuery("select o.address from Order o", Address.class).getResultList();
임베디드 타입 프로젝션의 한계는 어디에 소속 되있기 때문에 select address from Order 이런식으로는 조회가 안됩니다.
스칼라 타입 프로젝션
내가 원하는 데이터만 sql 짜듯이 가지고 올 수 있습니다. 일반 sql이랑 똑같다고 생각하면 됩니다.
em.createQuery("select distinct m.username, m.age from Member m").getResultList();
깔끔한거는 new 명령어로 조회하는 것 입니다. 마치 생성자를 호출하듯이 문법이 되있습니다. 물론 생성자는 dto에 정의 되어 있어야합니다. 패키지명이 길어지면 다 적어줘야합니다. 하지만 QueryDSL을 사용하면 극복가능합니다.
List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m")
.getResultList();
MemberDTO memberDTO = result.get(0);
'JPA' 카테고리의 다른 글
[JPA] JPQL(Java Persistence Query Language) 문법(2) (0) | 2020.04.30 |
---|---|
[JPA] 값 타입 컬렉션 (0) | 2020.04.26 |
[JPA] 값타입과 불변 객체 (0) | 2020.04.26 |
[JPA] 임베디드 타입(복합 값 타입) (0) | 2020.04.26 |
[JPA] 고아객체 (0) | 2020.04.22 |