📌 객체지향 쿼리 언어 소개
JPA는 다양한 쿼리 방법을 지원한다.
- JPQL
- JPA Criteria
- QueryDSL
- 네이티브 SQL
- JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용
JPQL - 소개
- 가장 단순한 조회 방법
- EntityManager.find()
- 객체 그래프 탐색(a.getB().getC())
- 나이가 18살 이상인 회원을 모두 검색하고 싶다면?
JPQL - 필요성
JPA를 사용하면 엔티티 객체를 중심으로 개발한다.
문제는 검색 쿼리이다. 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색한다.
그렇다고 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능하다.
결국 애플리케이션이 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL이 필요하다.
JPQL - 특징
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.
String jpql = "select m From Member m where m.name like '%hello%'";
SQL과 문법이 유사하며 SELECT, INSERT, FROM, WHERE, GROUP BY, HAVING, JOIN을 지원한다.
(JPQL은 엔티티 객체로 대상으로, SQL은 데이터베이스 테이블 대상으로 쿼리)
SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
Criteria - 소개
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
//Criteria 사용 준비
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);
//루트 클래스 (조회를 시작할 클래스)
Root<Member> m = query.from(Member.class);
//쿼리 생성
CriteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("username"), "kim"));
List<Member> resultList = em.createQuery(cq).getResultList()
- JPQL 빌더 역할
- JPA 공식 기능
- 너무 복잡하고 실용성이 없다.(실무에선 사용하지 않는다.)
- Criteria 대신 QueryDSL 사용을 권장한다.
QueryDSL 소개
//JPQL
//select m from Member m where m.age > 18
JPAFactoryQuery queryFactory= new JPAQueryFactory(em);
QMember m = QMember.member;
List<Member> list = queryFactory.selectFrom(m)
.where(m.age.gt(18))
.orderBy(m.name.desc())
.fetch()
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
- JPQL 빌더 역할
- 컴파일 시점에 문법 오류를 찾을 수 있음
- 동적쿼리 작성 편리
- 단순하고 쉬움
- 실무 사용 권장
네이티브 SQL 소개
- JPA가 제공하는 SQL을 직접 사용하는 기능
- JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능
ex. 오라클 CONNECT BY, 특정 DB만 사용하는 SQL 힌트
String sql ="SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";
List<Member> resultList = em.createNativeQuery(sql, Member.class)
.getResultList();
JDBC 직접 사용, SpringJdbcTemplate 등
JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, 스프링 JdbcTemplate, Mybatis 등 함께 사용가능
단, 영속성 컨텍스트를 적절한 시점에 강제로 플러시 필요.
(ex: JPA를 우회해서 SQL을 실행하기 직전 영속성 컨텍스트를 수동 플러시 해줘야 한다.)
📌 기본 문법과 쿼리 API
JPQL 소개
- JPQL은 객체지향 쿼리 언어다. 따라서 테이블을 대상으로 쿼리 하는 것이 아니라 엔티티 객체를 대상으로 쿼리 한다.
- JPQL은 SQL을 추상화해서 특정데이터베이스 SQL에 의존하지 않는다.(특정 DB에 의존하는 SQL을 작성하지 않아도 된다)
- JPQL은 결국 SQL로 변환된다.
@Entity
@Getter
@Setter
@ToString(exclude = "team")
@Table(name = "member")
public class Member {
@Id
@GeneratedValue
private Long id;
private String username;
private int age;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
}
@Entity
@Getter
@Setter
@Table(name = "team")
public class Team {
@Id @GeneratedValue
private Long id;
public String name;
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private List<Member> members = new ArrayList<>();
}
@Entity
@Getter
@Setter
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue
private Long id;
public int orderAmount;
@Embedded
private Address address;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
}
@Entity
@Getter
@Setter
@Table(name = "product")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
private int stockAmount;
}
@Embeddable
@Getter
@Setter
public class Address {
private String city;
private String street;
private String zipcode;
}
JPQL 문법
- 문법예시 : select m from Member as m where m.age > 18
- 엔티티와 속성은 대소문자 구분 O (Member, age)
- JPQL 키워드는 대소문자 구분 X (SELECT, FROM, where)
- 엔티티 이름 사용, 테이블 이름이 아님(Member)
- 별칭은 필수(m) (as는 생략가능)
집합과 정렬
- count(m), sum(m.age), avg(m.age), max(m.age), min(m.age)
- group by, having
- order by
TypeQuery, Query
- TypeQuery : 반환 타입이 명확할 때 사용
TypedQuery<Member> query = em.createQuery("SELECT m FROM Meber m", Member.class);
- Query : 반환 타입이 명확하지 않을 때 사용
Query query = em.createQuery("SELECT m.username, m.age FROM Meber m"); // m.username : Sring, m.age : int
결과 조회 API
- query.getResultList() : 결과가 하나 이상일 때, 리스트 반환
- 결과가 없으면 빈 리스트 반환
List<Member> resultList = em.createQuery("select m from Member m", Member.class).getResultList();
- query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환
- 결과가 없으면: javax.persistence.NoResultException
- 둘 이상이면: javax.persistence.NonUniqueResultException
Member singleResult = em.createQuery("select m from Member m", Member.class).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);
해당 글은 인프런의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 강의를 정리한 내용입니다.
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
'JPA > JPQL' 카테고리의 다른 글
[JPQL] 다형성 쿼리, 엔티티 직접 사용, Named 쿼리, 벌크 연산 (0) | 2023.04.26 |
---|---|
[JPQL] 경로 표현식, 페치 조인(N+1문제 해결) (1) | 2023.04.26 |
[JPQL] 타입 표현과 기타식, 조건식(CASE 등등), 기타 함수 (0) | 2023.04.25 |
[JPQL] 페이징, 조인, 서브쿼리 (0) | 2023.04.25 |
[JPQL] 프로젝션(SELECT) (0) | 2023.04.25 |
댓글