2026.05.07
Entity 객체를 대상으로 쿼리를 작성하는 객체 지향 SQL
| 항목 | SQL | JPQL |
|---|---|---|
| 대상 | 테이블 | 엔티티 |
| 반환 | 컬럼 값 | 객체 또는 객체 필드 |
| 문법 | 데이터베이스 종속 | JPA 구현체에 의해 처리됨 |
| 예시 | SELECT * FROM users | SELECT u FROM User u |
JPQL은 JPA 안에 속해 있는 쿼리 언어!
| 항목 | JPA | JPQL |
|---|---|---|
| 역할 | ORM 프레임워크 (자바 객체 ↔ DB) | JPA 위에서 동작하는 쿼리 언어 |
| 목적 | CRUD 전반 관리, 영속성 관리 | 엔티티 기준으로 데이터 조회 (Read 중심) |
| 예시 | persist(), merge(), remove() 등 API | @Query("SELECT u FROM User u") |
| SQL 사용 여부 | 직접 SQL 쓰지 않음 | SQL 유사 문법 (하지만 객체 기준) |
| 학습 난이도 | 낮음 (단순 CRUD만 하면) | 약간 있음 (문법 숙지 필요) |
| 상황 | 권장 여부 |
|---|---|
| 단순 CRUD | ✖️ (JPA 메서드 이름으로 충분함) |
| 복잡한 조건 조회 (WHERE, JOIN 등) | ✅ JPQL or QueryDSL |
| Native SQL 필요 (특정 DB 문법 등) | ✖️ NativeQuery 사용 |
| 동적 조건 필터링, 페이징 등 | ✅ QueryDSL or JPQL (가능하나 복잡함) |
SELECT u FROM User u; // 전체 유저 객체 조회
SELECT u.username FROM User u; // 특정 필드만 조회
SELECT u FROM User u WHERE u.age > 20;
SELECT u FROM User u ORDER BY u.age DESC;
SELECT * FROM orders o JOIN user u ON o.user_id = u.id WHERE u.username = "KIM";
| 로딩 방식 | EAGER (즉시 로딩) | LAZY (지연 로딩)⭐️ |
|---|---|---|
| 설명 | 연관된 엔티티를 즉시 로딩 | 실제 사용하는 시점에 로딩 |
| SQL 실행 시점 | 해당 엔티티 조회 시 바로 함께 조회 (즉시 Join) | getter 등으로 접근할 때 쿼리 실행 |
| 실무 기본 전략 | ❌ 잘 안 씀 (비효율적) | ✅ 실무 기본값 (JPA 기본 설정도 LAZY) |
1번 쿼리 결과로 나온 N개의 데이터를 각각 또 조회해서 총 N+1번 쿼리가 실행되는 현상!
연관된 엔티티를 한 번의 쿼리로 함게 조회하기 위한 JPQL 키워드
JOIN FETCH 키워드를 사용하면, 지연 로딩 설정이 되어 있더라고 즉시 로딩됨. (연관된 엔티티들을 SQL의 JOIN으로 함께 가져오기 때문에 추가 쿼리 발생 X)
JOIN FETCH 를 사용하면 Pageable 이 정상 동작하지 않음.DISTINCT 를 사용해서 중복된 엔티티 제거 필요 (JPQL에서는 객체 기준으로 중복을 제거함.)