2021. 12. 23. 19:05ㆍSpring/JPA
Spring Boot JPA JPQL 문제
JPQL은 만능이 아니다
- 되게 오랜만에 글을 쓰네요! 입사를 하고 나서 바쁘고,, 돈을 받으니 하고 싶었던걸 하다보니 블로그 작성을 하지 않았는데 .. 껄껄 다시 작성하려고 합니당
이번에 맞닥드린 문제는 JPQL를 사용하면서 데이터를 뽑아낼 때 처한 문제를 풀어내보려고 합니다. 일단 제목이 스포를 해버렸네요ㅎㅎ
조회 쿼리
Client에서 RequestParam으로 데이터를 받아서 해당 되는 데이터를 DTO로 핏하게 보내주는 작업을 하고 있었습니다.
@Query ( "SELECT new com.imaslab.fms.dtos.car.OnsiteServiceReceiverManageDTO(fu.id, fu.name, fu.phone, fc.name, rs.id, rs.name, COALESCE(orm.onsiteCancel, false ) , COALESCE(orm.onsiteWaiting, false ) , COALESCE(orm.onsiteConfirmed, false ) , COALESCE(orm.onsiteOperated, false )) " + "FROM FMSUser fu " + "LEFT JOIN OnsiteReceiverManage orm ON orm.fmsUser.id = fu.id " + "LEFT JOIN FMSCompany fc ON fc.id = fu.companyId " + "INNER JOIN fu.roles rs " + "WHERE rs.name = "INSTALLER" " + "AND coalesce(:userList, NULL) IS NULL OR (fu.id IN :userList) " + "ORDER BY orm.updatedAt desc " ) List<OnsiteServiceReceiverManageDTO> findReceiverManage(List<Long> userList);
이런식으로 조회 쿼리를 작성했습니다. User와 Role 테이블은 다대다 관계를 맺고 있어서 JOINTABLE로 연관관계를 맺었습니다. 이미 작성되어있는 코드여서 다대일, 일대다로 풀어내기가... 힘들어서 그대로 사용했습니다.
여기서 문제점은
WHERE 절에서 rs.name = INSTALLER
라는 조건문이 제가 원했던 USER라는 테이블에서 INSTALLER Role을 가진 사람만 조회를 하려고 했지만 그렇게 되지 않았고, INSTALLER의 ID가 5번이라면 5번 안쪽 ID들만 조회가 된는게 문제였습니다.디버깅을 하면서 추측한...문제는 현재 FMSUser에 들어있는 roles라는 클래스 변수는 Role타입인 List값입니다. 그래서
WHERE
절에서 Role객체를 꺼내와서Role.name = 'INSTALLER'
이런식으로 표현이 되어야하는데 SQL 에서는 작성이 되고 데이터도 표출이 잘 된다.질문을 하고 피드백을 받은 것 중 하나가 JPQL은 JAVA에서 객체를 SQL로 다룰 수 있도록 한거지 SQL이 아니기 때문에 '만능' 이 아니야 라고 말씀을 하셨고 굳이 조건절을 위해서 SQL에서 씨름을 하지말고 객체를 받아서 Service단에서 조건절을 해주는건 어때? 라고 피드백을 받았습니다. 뭔가 제일 아... 하게 되는 부분이어서 무릎을 탁! 쳤네요..!
그래서 다음은 수정한 쿼리이고
@Query ( "SELECT new com.imaslab.fms.dtos.car.OnsiteServiceReceiverManageDTO(fu.id, fu.name, fu.phone, fc.name, rs.id, rs.name, COALESCE(orm.onsiteCancel, false ) , COALESCE(orm.onsiteWaiting, false ) , COALESCE(orm.onsiteConfirmed, false ) , COALESCE(orm.onsiteOperated, false )) " + "FROM FMSUser fu " + "LEFT JOIN OnsiteReceiverManage orm ON orm.fmsUser.id = fu.id " + "LEFT JOIN FMSCompany fc ON fc.id = fu.companyId " + "INNER JOIN fu.roles rs " + "WHERE coalesce(:userList, NULL) IS NULL OR (fu.id IN :userList) " + "ORDER BY orm.updatedAt desc " ) List<OnsiteServiceReceiverManageDTO> findReceiverManage(List<Long> userList);
다음은 Service입니다.
fmsUserRepository.findReceiverManage(StringToLongListSearchUsers.size() == 0 ? null : StringToLongListSearchUsers) .stream() .filter(onsiteServiceReceiverManageDTO -> onsiteServiceReceiverManageDTO.getInfoDTO().getRoleName().equals(role)) .collect(Collectors.toList());
결론
- 이 글의 제목에 내용이 스포가 되었지만 JPQL은 만능이 아니고 우리는 객체를 다룰 수 있으니 유연하게 사고하자.
'Spring > JPA' 카테고리의 다른 글
Bulk Excel Data Insert 1부 (0) | 2022.06.20 |
---|---|
Java8 Stream 특정 Key 중복제거 (0) | 2022.05.30 |
[Spring JPA] 즉시로딩과 지연로딩 (0) | 2021.06.28 |
Spring JPA [상속관계 맵핑] (0) | 2021.06.17 |
[Spring JPA] 다양한 연관관계 매핑 (0) | 2021.04.19 |