ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • #Spring Data Jpa - 14 Projections
    Spring Data JPA 2021. 3. 26. 09:50

    Projections(쿼리 select 절에 들어갈 데이터 <가져올 필드>)

    https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

    엔티티 대신에 DTO를 편리하게 조회할 때 사용 전체 엔티티가 아니라 만약 회원 이름만 딱 조회하고 싶으면?

     

    Spring Data JPA - Reference Documentation

    Example 109. Using @Transactional at query methods @Transactional(readOnly = true) public interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") v

    docs.spring.io

    public interface UsernameOnly {
     	String getUsername();
    }

    인터페이스만 만들면 Spring Data JPA가 구현체를 직접 만들어준다. username속성만 가지고오면 되는구나 해서 구현체에 username을 담에서 반환해준다.

    조회할 엔티티의 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회(Projection)

     

    public interface MemberRepository ... {
    	 List<UsernameOnly> findProjectionsByUsername(String username);
    }

    메서드 이름은 자유, 반환 타입으로 인지

     

    @Test
    public void projections() throws Exception {
         //given
         Team teamA = new Team("teamA");
         em.persist(teamA);
         Member m1 = new Member("m1", 0, teamA);
         Member m2 = new Member("m2", 0, teamA);
         
         em.persist(m1);
         em.persist(m2);
         
         em.flush();
         em.clear();
         
         //when
         List<UsernameOnly> result =
        memberRepository.findProjectionsByUsername("m1");
        
         //then
         Assertions.assertThat(result.size()).isEqualTo(1);
    }
    select m.username from member m
    where m.username=‘m1’;

    SQL에서도 select절에서 username만 조회(Projection)하는 것을 확인

     

    인터페이스 기반 Closed Projections 프로퍼티 형식(getter)의 인터페이스를 제공하면, 구현체는 스프링 데이터 JPA가 제공

    public interface UsernameOnly {
       String getUsername();
    }

     

    인터페이스 기반 Open Proejctions

    다음과 같이 스프링의 SpEL 문법도 지원

    public interface UsernameOnly {
       @Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
       String getUsername();
    }

      open projection

        - spl을 쓰며 멤버를 다가져온다 거기에서 SpEL을 계산함,둘다가져와서 문자로 더해서 넣어줌 spring SpEL문법 
      close projection

       - 정확하게 매칭 select 절이 최적회됨

     

    단! 이렇게 SpEL문법을 사용하면, DB에서 엔티티 필드를 다 조회해온 다음에 계산한다! 따라서 JPQL SELECT 절 최적화가 안된다.

     

    클래스 기반 Projection

    다음과 같이 인터페이스가 아닌 구체적인 DTO 형식도 가능 생성자의 파라미터 이름으로 매칭

    public class UsernameOnlyDto {
       private final String username;
       
       public UsernameOnlyDto(String username) {
       	this.username = username;
       }
       
       public String getUsername() {
       	return username;
       }
    }

    동적 Projections

    다음과 같이 Generic type을 주면, 동적으로 프로젝션 데이터 번경 가능

    <T> List<T> findProjectionsByUsername(String username, Class<T> type);

    쿼리가 똑같고 어쩔때는 username 어쩔떄는 userid를 가지고오고 싶을때 사용

     

    사용코드

    List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1",UsernameOnly.class);

    뭔가 엔티티가 아니라 다른게 들어왓네 하고 확인하고, 생성자의 파라미터 명을 분석한다.

    파라미터 명이 달라지면 안된다.

     

    중첩 구조 처리

    public interface NestedClosedProjection {
       String getUsername();
       TeamInfo getTeam();
       
       interface TeamInfo {
         String getName();
       }
    }
    select
         m.username as col_0_0_,
         t.teamid as col_1_0_,
         t.teamid as teamid1_2_,
         t.name as name2_2_
    from
         member m
         left outer join
         team t
         on m.teamid=t.teamid
    where
         m.username=?

    주의

    · 프로젝션 대상이 root 엔티티면, JPQL SELECT 절 최적화 가능

    · 프로젝션 대상이 ROOT가 아니면

        · LEFT OUTER JOIN 처리

        · 모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산

     

     

    정리

    · 프로젝션 대상이 root 엔티티면 유용하다.

    · 프로젝션 대상이 root 엔티티를 넘어가면 JPQL SELECT 최적화가 안된다!

    · 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.

    · 실무에서는 단순할 때만 사용하고, 조금만 복잡해지면 QueryDSL을 사용하자

     

     

    댓글

Designed by Tistory.