목표: like가 많은수대로 내림차순으로 이미지를 보이게 할거다.
ImageController.class
@RequiredArgsConstructor
@Controller
public class ImageController {
private final ImageService imageService;
private final UserService userService;
...
@GetMapping({"/image/popular"})
public String popular(Model model){
List<Image> images =imageService.popularImg();
model.addAttribute("images", images);
return "image/popular";
}
...
}
ImageService.class
@RequiredArgsConstructor
@Service
public class ImageService {
private final ImageRepository imageRepository;
@Transactional(readOnly = true)
public List<Image>popularImg(){
return imageRepository.mPopular();
}
...
}
ImageRepository
public interface ImageRepository extends JpaRepository<Image, Integer> {
...
@Query(value = "SELECT i.* FROM image i INNER JOIN(SELECT imageId, COUNT(imageId) likeCount FROM likes GROUP BY imageId) c ON i.id = c.imageId ORDER BY likeCount DESC", nativeQuery = true)
List<Image> mPopular();
...
}
이미지 인기순 구하는 쿼리짜기
likes 테이블에 보면 3번 이미지는 2개의, 2번 이미지는 1개의 like을 받은 것을 알 수 있다.
따라서 전체 이미지 중 3번-> 2번 순으로 2개의 이미지만 구해져야한다.
likes 테이블에 imageId 와 likeCount라는 가상컬럼을 만든다.
imageId가 중복으로 나오므로 imageId를 기준으로 GROUP BY 해서 묶어준다.
imageId의 수 만큼을 구해져야 함으로 imageId 를 기준으로 COUNT 해주고 이 결과를 DESC해서 조회한다.
이렇게 구해진 쿼리를 위의 IN에 넣으려 하면 오류가 생긴다.
IN에는 ID값만 들어가야 하는데 방금 구한 쿼리는 likeCount 값도 포함되있기 때문이다.
이때 인라인 뷰를 활용한다.
인라인 뷰란 FROM 절에서 사용하는 서브쿼리로 ,
FROM 절에서 서브쿼리 자체가 하나의 테이블처럼 사용된다.
그렇게 만들어진 인라인 뷰를
WHERE IN 절에 넣었는데, 내리차순이 적용안됐다!
사실 IN연산자에 3,2 로 순서를 바꿔서 넣어도 오름차순으로 반영되서 조회되기 때문에
IN 연산자로 해결할수 없다.
따라서 다른 방법으로 JOIN해야한다.
그래서 아래 SELECT절로 구해진 imageId와 위 SELECT절의 id 값가 같은것만 조인한다.
따라서 아래와 같이 inner join한다.
== 완성==
ImageRepository 에서는 쿼리의 반환타입이 Image 클래스이기 때문에 image 테이블이 가지고 있는 값만 구한다.
popular.html
타임리프 반복문을 통해 이미지 정보를 노출시킨다.
...
<!--인기게시글 갤러리(GRID배치)-->
<div class="popular-gallery">
<tr th:each="image : ${images}">
<div class="p-img-box">
<a href="/user/${image.user.id}" th:href="|/user/${image.user.id}|" >
<img src="/images/home.jpg" th:src="${image.postImageUrl}" />
</a>
</div>
</tr>
</div>
...