본문 바로가기
Backend/JPA

JPA (Spring Data JPA) - 쿼리 메소드 기능 (예제 코드)

by 개발자-제이 2025. 3. 11.

 

1. Spring Data JPA 쿼리 메소드 개요

Spring Data JPA는 JPA를 더 쉽고 편리하게 사용할 수 있도록 지원하는 프레임워크다. 기본적인 CRUD 기능뿐만 아니라 자동으로 쿼리를 생성하고 실행하는 기능도 제공한다.

대표적인 쿼리 생성 방식

  1. 메소드 이름 기반 쿼리: findByUsernameAndAgeGreaterThan
  2. NamedQuery: @NamedQuery 활용
  3. @Query 어노테이션 활용: 직접 JPQL 작성

 

2. 쿼리 메소드 3가지 유형

 

유형 설명 예제
메소드 이름 기반 쿼리 메소드 이름을 분석하여 JPQL 자동 생성 findByUsernameAndAgeGreaterThan
NamedQuery 엔티티 클래스에 미리 정의된 JPQL 쿼리를 사용 @NamedQuery(name="Member.findByUsername")
@Query 어노테이션 JPQL을 직접 작성하여 실행 @Query("SELECT m FROM Member m WHERE m.username = :username")

 

3. 메소드 이름 기반 쿼리

Spring Data JPA는 메소드 이름을 기반으로 자동으로 JPQL을 생성한다.

순수 JPA 방식

public List<Member> findByUsernameAndAgeGreaterThan(String username, int age) {
    return em.createQuery("SELECT m FROM Member m WHERE m.username = :username AND m.age > :age")
            .setParameter("username", username)
            .setParameter("age", age)
            .getResultList();
}

Spring Data JPA 방식

public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}

자동 생성되는 JPQL

SELECT m FROM Member m WHERE m.username = ?1 AND m.age > ?2

쿼리 키워드 목록

키워드 설명
findBy 조회
countBy 개수 조회
existsBy 존재 여부 확인
deleteBy 삭제
top, first 제한된 개수 조회

 

 

4. NamedQuery 활용

NamedQuery 정의

@Entity
@NamedQuery(
    name = "Member.findByUsername",
    query = "select m from Member m where m.username = :username"
)
public class Member {
    ...
}

NamedQuery 호출

( 순수 JPA 방식 )

public class MemberRepository {
    public List<Member> findByUsername(String username) {
        return em.createNamedQuery("Member.findByUsername", Member.class)
                .setParameter("username", username)
                .getResultList();
    }
}

( Spring Data JPA 방식 )

@Query(name = "Member.findByUsername")
List<Member> findByUsername(@Param("username") String username);

- 메서드 이름만으로도 NamedQuery를 자동으로 호출 가능.

 

5. @Query 어노테이션 활용

@Query 직접 사용

public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query("SELECT m FROM Member m WHERE m.username = :username AND m.age = :age")
    List<Member> findUser(@Param("username") String username, @Param("age") int age);
}

장점

  • 복잡한 쿼리도 쉽게 정의 가능
  • JPQL 문법 오류를 애플리케이션 실행 전에 감지 가능

 

6. DTO 조회

DTO를 직접 조회

@Query("SELECT new com.example.MemberDto(m.id, m.username, t.name) FROM Member m JOIN m.team t")
List<MemberDto> findMemberDto();

DTO 클래스

@Data
public class MemberDto {
    private Long id;
    private String username;
    private String teamName;
    
    public MemberDto(Long id, String username, String teamName) {
        this.id = id;
        this.username = username;
        this.teamName = teamName;
    }
}

 

 

7. 페이징과 정렬

페이징 메소드

public interface MemberRepository extends JpaRepository<Member, Long> {
    Page<Member> findByAge(int age, Pageable pageable);
}

페이징 실행 코드

PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));
Page<Member> page = memberRepository.findByAge(10, pageRequest);

 

 

8. 벌크 연산

대량의 데이터를 수정하는 경우 @Modifying 어노테이션을 사용한다.

대량 수정 쿼리

@Modifying
@Query("UPDATE Member m SET m.age = m.age + 1 WHERE m.age >= :age")
int bulkAgePlus(@Param("age") int age);
@Modifying(clearAutomatically = true)

주의: 벌크 연산은 영속성 컨텍스트를 무시하고 실행되므로, 영속성 컨텍스트를 초기화해야 한다.

 

9. @EntityGraph (N+1 문제 해결)

Spring Data JPA는 @EntityGraph를 사용하여 연관된 엔티티를 한 번에 조회할수도 있다.

@EntityGraph(attributePaths = {"team"})
List<Member> findByUsername(String username);

-  페치 조인(FETCH JOIN)과 유사한 기능 제공.

 

10. JPA 힌트 & 락

JPA 힌트 사용

@QueryHints(value = @QueryHint(name = "org.hibernate.readOnly", value = "true"))
Member findReadOnlyByUsername(String username);
  • 조회 전용(read-only)로 설정하면 성능이 최적화됨.

락(Lock) 사용

@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findByUsername(String name);
  • 동시성 문제를 해결하기 위해 Pessimistic Lock 사용 가능.
반응형