JPA(Java persistence API)를 사용하여 서비스를 구현하다 보면 JPA에서 제공하는 기능만으로는 조회가 불가능한 경우가 존재한다.
대표적으로 엔티티 객체를 그대로 조회하는 것이 아닌 DTO에 맞춰서 조회하는 경우이다.
[ 예시 / 인스타그램 클론코딩 중 구독 정보 조회 커리 ]
SELECT u.id, u.username, u.profileImageUrl,
if((SELECT 1 FROM subscribe
WHERE fromUserId = 1 AND toUserId = u.id),1,0) subscribeState,
if((1=u.id),1,0) equalUserState
FROM user u INNER JOIN subscribe s
ON u.id = s.toUserId
WHERE s.fromUserId = 2;
이런식으로 필요한 데이터를 쿼리로 작성하고 스프링에 대입해주면 된다.
여러가지 방법이 존재하지만 손쉽게 할 수 있는 QLRM 라이브러리를 사용해보려고 한다.
[ build.gradle ]
implementation group: 'org.qlrm', name: 'qlrm', version: '3.0.4'
[ SubscribeSerive.java ]
@RequiredArgsConstructor
@Service
public class SubscribeService {
private final SubscribeRepository subscribeRepository;
private final EntityManager em; // Repository는 EntityManager를 구현해서 만들어져 있는 구현체
@Transactional(readOnly = true)
public List<SubscribeDto> 구독리스트(int principalId, int pageUserId) {
// 쿼리 준비
StringBuffer sb = new StringBuffer();
sb.append("SELECT u.id, u.username, u.profileImageUrl, ");
sb.append("if ((SELECT 1 FROM subscribe WHERE fromUserId = ? AND toUserId = u.id), 1, 0) subscribeState, "); // 물음표 principalId
sb.append("if ((?=u.id), 1, 0) equalUserState "); // 물음표 principalId
sb.append("FROM user u INNER JOIN subscribe s ");
sb.append("ON u.id = s.toUserId ");
sb.append("WHERE s.fromUserId = ?"); // 물음표 pageUserId
// 쿼리 완성
Query query = em.createNativeQuery(sb.toString())
.setParameter(1, principalId)
.setParameter(2, principalId)
.setParameter(3, pageUserId);
// 쿼리 실행 (qlrm 라이브러리 필요 = Dto에 DB결과를 매핑하기 위해서)
JpaResultMapper result = new JpaResultMapper();
List<SubscribeDto> subscribeDtos = result.list(query, SubscribeDto.class);
return subscribeDtos;
}
}
- 해당 쿼리가 리턴하는 값이 Subscribe 타입이 아니기 때문에 사용하려는 Service에서 직접 Native Query를 작성해주어야한다.
-> 그러기 위해 EntityManager를 DI 해준다. - 쿼리 준비를 위한 StringBuffer를 만들고 필요한 파라미터를 변수 바인딩 해준다.
-> 변수를 ?로 바인딩하고, em.createNativeQuery를 이용하여 각각 변수를 바인딩 해줄 수 있다. - 해당 쿼리를 실행해주기 위해 JpaResultMapper를 사용해준다
-> QLRM이 지원해주는 메소드이다.
해당 코드가 잘 작동하면 아래와 같이 데이터가 불러와진다.