spring/JPA

[JPA] 객체 지향 쿼리 언어 JPQL 3 (기본 문법 - 프로젝션, 페이징)

대기업 가고 싶은 공돌이 2024. 7. 31. 02:12

프로젝션

프로젝션이란 select 절에 조회할 대상을 지정하는 것이다.

 

프로젝션 대상: 엔티티, 임베디드 타입, 스칼라 타입 모든 것이 가능하다.

(SQL 은 기본 데이터 타입만 가능함)

 

select m from Member m -> 엔티티 프로젝션

 

select m.team from Member m -> 엔티티 프로젝션 (멤버 엔티티와 연관된 Team 테이블을 가져옴)

 

더보기

select m.team from Member m 은 SQL에서 member테이블과 team 테이블을 조인 시킨 후에 결과를 가져온다.

 

위의 jpql만 보면 조인 쿼리가 나가는 것인지 아닌지 바로 알아차리기 힘들기 때문에,

 

유지 보수성을 위해 select m.team from Member m 대신 select T from Member m join m.team T 와 같은 형식으로 작성해 주자.

 

엔티티 프로젝션에서 가져온 엔티티들은 모두 영속성 컨텍스트에서 관리가 된다.

즉 값을 바로 수정해도 다 DB에 반영이 된다는 의미다.

 

select m.address from Member m -> 임베디드 타입 프로젝션 (여기서 address 는 임베디드 타입이다.)

더보기

address는 임베디드 타입으로 테이블 생성시 Member 테이블에 값이 다 들어가기 때문에 join 쿼리가 발생하지 않는다.

select m.username, m.age from Member m -> 스칼라 타입 프로젝션 

 

앞에 distinct를 추가하면 중복 제거가 가능하다.

 

프로젝션 여러 값 조회

 

  1. Query 타입으로 조회
  2. Object[] 타입으로 조회
  3. new 명령어로 조회
    • 단순 값을 DTO로 바로 조회
      select new jpabook.jpql.UserDTO(m.username, m.age) from Member m, UserDTO.class
    • 패키지 명을 포함한 전체 클래스 명을 입력해야 한다.
    • 순서와 타입이 일치하는 생성자가 필요하다.

Spring Data jpa에서 사용하는 방법

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select new jpabook.jpql.UserDTO(m.username, m.age) from Member m")
    List<UserDTO> findUserDTOs();
}

 

가장 많이 사용하는 방식은 3번이다. 

 

그러나 패키지 명이 길어져도 다 적어줘야한다는 단점이 존재한다.

이러한 단점은 QueryDSL을 사용하면 극복이 가능하다.

 

페이징

jpa는 페이징을 다음 두 API로 추상화 시켰다.

 

  1. setFirstResult(int start Position): 조회 시작위치 (0부터 시작)
  2. setMaxResults(int maxResult): 조회할 데이터 수
em.createQuery("select m from Member m order by m.age desc, Member.class)
	.setFirstResult(0)
    .setMaxResult(10)
    .getResultList();

 

Spring Data jpa에서 사용하는 방법

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("SELECT m FROM Member m ORDER BY m.age DESC, m.className")
    Page<Member> findAllMembersWithPagination(Pageable pageable);
}

 

@Service
public class MemberService {

    @Autowired
    private MemberRepository memberRepository;

    public Page<Member> getMembersWithPagination(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return memberRepository.findAllMembersWithPagination(pageable);
    }
}

 

다음과 같은 방식으로 페이징을 구현할 수 있다.