JPA가 기본 제공하는 객체 쿼리 언어. SQL과 비슷하지만 테이블이 아닌 엔티티를 대상으로 한다.
@Query("SELECT m FROM Member m WHERE m.age > :age AND m.team.name = :teamName")
List<Member> findByCondition(@Param("age") int age, @Param("teamName") String teamName);
한계 : 문자열이라 컴파일 타임에 오류를 잡을 수 없고, 동적 쿼리 작성이 불편하다.
DB에 직접 날리는 순수 SQL. 복잡한 쿼리나 DB 종속 기능이 필요할 때 사용한다.
@Query(value = "SELECT m.*, COUNT(o.id) as order_count " +
"FROM members m " +
"LEFT JOIN orders o ON m.id = o.member_id " +
"GROUP BY m.id " +
"HAVING order_count >= :minOrders",
nativeQuery = true)
List<Object[]> findActiveMembers(@Param("minOrders") int minOrders);
타입 안전한 동적 쿼리를 자바 코드로 작성할 수 있다. 가장 널리 쓰이는 조합.
public List<Member> searchMembers(String name, Integer minAge, String teamName) {
BooleanBuilder builder = new BooleanBuilder();
if (name != null) builder.and(member.name.contains(name));
if (minAge != null) builder.and(member.age.goe(minAge));
if (teamName != null) builder.and(member.team.name.eq(teamName));
return queryFactory
.selectFrom(member)
.leftJoin(member.team, team).fetchJoin()
.where(builder)
.orderBy(member.id.desc())
.fetch();
}
장점 : 컴파일 타임 오류 체크, 자동완성, 조건 조합이 자유로움
프로젝트에서 JPA와 MyBatis를 같이 쓰는 것도 가능하다. 통계나 리포트처럼 복잡한 조회는 MyBatis로, 일반 CRUD는 JPA로 분리하는 패턴.
| 상황 | 추천 방식 |
|---|---|
| 단순 CRUD | Spring Data JPA 메서드 |
| 조건 검색, 동적 쿼리 | QueryDSL |
| DB 함수, 윈도우 함수 등 | Native Query |
| 대량 INSERT/UPDATE | Native Query 또는 JDBC 직접 사용 |
| 복잡한 통계 / 리포트 | Native Query 또는 MyBatis 혼용 |
핵심은 유연하게, 적재적소에 사용하는 것. JPA를 쓴다고 해서 모든 걸 ORM으로만 해결해야 하지 않음. 단순한 것은 JPA에게 위임하고, 복잡한 것은 직접 쿼리를 작성하는 것이 현실적인 접근임.