기존에 이 글도 공감해주세요 게시판 코드를 refactoring했다.
2022.12.01 - [TIL] - 이 글도 공감해주세요 게시판 기능 수정
기존 코드
RegistryRepository
@Query("select r.idx from Registry r")
ArrayList<Long> findAllByIdx();
CommentRepository
@Query("select c.registry.idx from Comment c")
ArrayList<Long> findAllByRegistry_Idx();
CommentServiceImpl.java
public List<Optional<Registry>> needComments() {
List<Long> allByIdx = registryRepository.findAllByIdx();
List<Long> allByRegistry_idx = commentRepository.findAllByRegistry_Idx();
List<Long> temp = new LinkedList<>();
temp.addAll(allByIdx);
for (Long item : allByIdx) {
if (allByRegistry_idx.contains(item) == true) {
temp.remove(item);
}
}
List<Optional<Registry>> result = new ArrayList<>();
if (temp.isEmpty()) {
result = Collections.emptyList();
}
if (temp.toArray().length > 10) {
for (int i = 0; i < 10; i++) {
result.add(registryRepository.findById(temp.get(i)));
}
} else {
for (int i = 0; i < temp.toArray().length; i++) {
result.add(registryRepository.findById(temp.get(i)));
}
}
return result;
}
특히 ServiceImpl에서 if문이 너무 많아서 이를 줄이려고 생각하다가 refactoring 하게 되었다.
먼저 RegistryRepository에 작성된 코드를 지웠다.
그리고 CommentRepsotiroy에서 모든걸 다 처리할 생각이었다.
Registry의 idx값 - Comment.registry.idx 값 이므로 차집합을 이용하면 될 것 같았다. 참고한 블로그
SELECT r.registry_id FROM Registry r WHERE r.registry_id NOT IN (SELECT DISTINCT c.registry_id from Comment c) Limit 10;
만약 NOT IN을 사용했을 때 오류가 떴다면 해당 데이터의 null이 있는지 확인해본다.
처음에 값이 안떠서 왜 안뜨는지 확인해보니 기존에 db를 만들고 난 후에
연관관계 매핑을 하면서 registryId 컬럼을 추가했고 기존에 있던 db들의 registryId값은
null로 띄워져서 제대로 실행이 되지 않았었다.
그리고 NOT IN을 이용한 서브쿼리는 성능이 좋지 않다고 하므로 아래 코드로 작성했다. https://toe10.tistory.com/156
SELECT r.registry_id FROM Registry r LEFT JOIN Comment ON r.registry_id = comment.registry_id WHERE Comment.registry_id is null Limit 10;
LIMIT는 반환되는 행의 개수를 제한한다.
LIMIT 10을 주어서 10개까지만 처리되게 했다. → ServiceImpl에서 if문을 생략할 수 있다.
10개 보다 작은 데이터가 있으면 해당하는 데이터의 개수만큼만 보여준다. (최대 행수가 10개까지 라는 것)
but, LIMIT는 MySQL과 PostgreSQL에서 사용할 수 있는 문법이다.
따라서 JPQL에서는 사용할 수 없다.
[SQL 첫걸음] 11. 결과 행 제한하기 - LIMIT
따라서 LIMIT와 같이 제한을 하고 싶다면 Top을 이용해서
쿼리가 아니라 메소드 명을 findTop10By(); 와 같이 작성을 해야한다.
https://stackoverflow.com/questions/44565820/what-is-the-limit-clause-alternative-in-jpql
하지만 실제 test해보니 잘 동작하지 않았고 paging을 이용해서 사용해야 할 듯 하다.
위에서 sql console로 실행했을 때 잘 동작하는 것을 보고 JPQL언어로 수정했다.
@Query("SELECT r.idx FROM Registry r LEFT JOIN Comment ON r.idx = Comment.registry.idx WHERE Comment.registry.idx is null")
* JPQL에서 사용하는 Member는 클래스 명이 아니라 엔티티 명이다.
JPQL로 작성하면 엔티티 클래스를 바라보고 작성을 하기 때문에
이후에 SQL 대신 다른 언어로 바꿔도 적용이 되기 때문에 언어에 의존적이지 않게 작성할 수있다.
* db 언어로 쓸수는 있지만 db를 변경하게 되면 다시 언어를 바꿔 써야 한다.
https://haedal-uni.github.io/posts/JPQL/
그런데 실행을 해보니
Could not create query for public abstract Validation failed for query for method public abstract ~
cannot read field value because s1 is null
이런 에러가 계속 떴다.
대부분 해당 오류를 nativeQuery = true 로 보고 있었다.
직접 쿼리를 날리는 경우라면 NativeQuery를 True로 설정해주면 된다고 한다.
sql문으로 작성한게 아니라 JPQL로 작성한 것인데 왜 해당 오류가 뜬건지는 모르겠으나
혹시몰라 SQL 언어로 수정하여 실행했더니 동작했다.
🤔이 부분에 대해서 좀 더 찾아봐야겠다.
알고보니 Alias를 안써서 생긴 오류였다. JPQL에서는 Alias를 필수로 사용해야한다..!!
@Query("SELECT r.idx FROM Registry r LEFT JOIN Comment c ON r.idx = c.registry.idx WHERE c.registry.idx is null")
limit는 JPQL에서는 사용할 수 없기 때문에 paging으로 작성했다.
ServiceImpl
Pageable pageable = PageRequest.of(0, 10);
List<Long> temp = commentRepository.findTop10By(pageable);
Repository
@Query("SELECT r.idx FROM Registry r LEFT JOIN Comment c ON r.idx = c.registry.idx WHERE c.registry.idx is null")
List<Long> findTop10By(Pageable pageable);
Pageable : 페이지 처리에 필요한 정보를 담게 되는 인터페이스
PageRequest에 의해 Pageable에 페이징 정보가 담겨 객체화 된다.
PageRequest의 메서드
- of(int page, int size) : 0부터 시작하는 페이지 번호와 개수. 정렬이 지정되지 않음
- of(int page, int size, Sort sort) : 페이지 번호와 개수, 정렬 관련 정보
JPA Paging(Page, Pageable, PageRequest, Sort)
pr - [refactoring] postComment
아래는 sql 언어로 사용했을 때 코드로 작성했다.
@Query어노테이션에 들어있는 nativeQuery라는 속성을 이용하여
JPQL로 작성한 것인지 SQL로 작성한 것인지를 구분할 수 있다.
- nativeQuery = true → SQL
- nativeQuery = false (default) → JPQL
@Query(value = "SELECT r.registry_id FROM Registry r LEFT JOIN Comment ON r.registry_id = comment.registry_id WHERE Comment.registry_id is null Limit 10;", nativeQuery = true)
List<Long> findTop10By();
위에서 최대 10개의 id값을 가지고 오니
ServiceImpl에서는 해당 id에 맞는 Resigtry db를 가져오는 코드만 작성하면 끝난다.
public List<Optional<Registry>> needComments() {
List<Long> temp = commentRepository.findTop10By();
List<Optional<Registry>> result = new ArrayList<>();
for (int i = 0; i < temp.toArray().length; i++) {
result.add(registryRepository.findById(temp.get(i)));
}
return result;
}
쿼리문을 활용해 db를 가지고 오니 코드가 훨씬 간결해졌다.
[pr - [refactoring] 이 글도 공감해주세요 ]
'TIL' 카테고리의 다른 글
채팅 재연결 (0) | 2022.12.28 |
---|---|
프로젝트 - 연관관계 매핑 끝 (0) | 2022.12.20 |
findById vs getReferenceById (0) | 2022.12.12 |
가설 (0) | 2022.12.10 |
스터디 요약 (0) | 2022.12.04 |