본문 바로가기
spring/spring jpa

[SpringBoot] JPA Projection

by moonsiri 2021. 6. 23.
728x90
반응형

Projection은 Entity의 속성이 많을 때 일부 데이터만 조회하는 방법입니다.

 

아래 UserEntity를 참고하여 설명하겠습니다.

@Entity
@Table(name = "user")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserEntity {

    @Id
    @Column(name = "user_id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;

    @Column(length = 50, nullable = false)
    private String name;

    @Column(length = 50, nullable = false)
    private String lastname;

    private int age;

    @Column(name = "email_address", length = 200)
    private String emailAddress;
    
}

 

 

 

1. Closed Projections

 

name과 lastname만 조회하고 싶다고 가정하여 인터페이스를 생성합니다.

public class UserDTO {

    public interface UserSummary {
        String getName();
        String getLastname();
    }
}

 

만든 인터페이스 타입으로 쿼리를 만들고 실행시켜주면

public interface UserRepository extends JpaRepository<UserEntity, Long> {
    UserDTO.UserSummary findByLastname(String lastname);
    List<UserDTO.UserSummary> findAllByAge(int age);
}

name과 lastname만 조회해오는 것을 확인할 수 있습니다.

 

 

2. Open Projections

 

open projections는 모든 요소를 조회해와서 두 개의 요소를 합쳐서 보여줄 수 있습니다. 따라서 성능 최적화는 불가능합니다.

public class UserDTO {

    public interface UserSummary {
        String getName();
        String getLastname();
        
        @Value("#{target.name + ' ' + target.lastname}")
        String getFullName();
    }
}

 

그래서 아래와 같이 필요한 요소만 가져와 커스텀할 수 있습니다.

public class UserDTO {

    public interface UserSummary {
        String getName();
        String getLastname();
        
        default String getFullName() {
            return getName().concat(" ").concat(getLastname());
        }
    }
}

 

 

3. Dynamic Projections

 

아래와 같이 두 가지 타입의 Projection으로 값을 받고 싶을 때

public class UserDTO {

    public interface UserSummary {
        String getName();
        String getLastname();
    }

    public interface AgeOnly {
        int getAge();
    }
}

 

메서드 명이 같으면 오류가 발생합니다. 이런 경우 Generic 타입을 사용하면 됩니다.

UserDTO.UserSummary result1 = userRepository.findByLastnameEquals(lastname, UserDTO.UserSummary.class);
UserDTO.AgeOnly result2 = userRepository.findByLastnameEquals(lastname, UserDTO.AgeOnly.class);

 

 

4. QueryDsl에서 Projection 사용

public class UserDTO {

    @Getter @Setter
    @NoArgsConstructor
    public static class GetRes {
        private String name;
        private String lastname;
        private int age;
        
        public GetRes(String name, String lastname, int age) {
            this.name = name;
            this.lastname = lastname;
            this.age = age;
        }
    }
}
@RequiredArgsConstructor
public class UserRepositoryCustomImpl implements UserRepositoryCustom {

    private final JPAQueryFactory queryFactory;
    
    private static final QUserEntity userEntity = QUserEntity.userEntity;
    
    @Override
    public List<UserDTO.GetRes> findAllOrderByIdDesc(UserDTO.GetListReq param) {
    	return queryFactory.select(Projections.constructor(UserDTO.GetRes.class,
            userEntity.userId, userEntity.userName, userEntity.age))
        	           .from(userEntity)
        	           .where(userEntity.age.eq(param.getAge()))
        	           .orderBy(userEntity.id.desc())
        	           .fetch();
    }
    
}

 

 

 

[Reference]

https://docs.spring.io/spring-data/commons/docs/2.5.2/reference/html/#projections

728x90
반응형

댓글