복잡한 쿼리짜는 법
보드 쿼리로 이동
public void selectV1() {
//게시글마다의 댓글 개수 뽑는 쿼리
//데이터 보면
String sql = """
select *
from board_tb
""";
Query query = em.createNativeQuery(sql);
}
db더미
insert into board_tb(title, content, created_at, user_id)
values ('제목1', '내용1', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목2', '내용2', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목3', '내용3', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목4', '내용4', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목5', '내용5', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목6', '내용6', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목7', '내용7', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목8', '내용8', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목9', '내용9', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목10', '내용10', now(), 2);

쿼리짜기
1 제목5 3
2 제목4 1
3 제목3 0 이렇게 뽑아야함
쿼리
→
여기서 댓글 카운트만 뽑으면 된다
select id, title from board_tb
댓글 카운트 뽑는 쿼리
→
select * from reply_tb where board_id =5;

count로 친다
select count(id) from reply_tb where board_id =5;
3 나온다
2개 쿼리 합치는 방법
→
select id, title , 5
from board_tb;

합치면 이런거를 스칼라 서브쿼리라고 한다 (스칼라 → 1개 ) 즉 한개만 가능하다!
select id, title , select count(id) from reply_tb where board_id =5
from board_tb;

select comment from reply_tb where board_id =5;

컬럼도 1개 야 하고 행도 1개여야 한다!
컬럼이 2개면 2차원
select id, title , select comment from reply_tb where board_id =5
from board_tb;
board에서 10개 퍼올렸어
제목 10부터 1까지 10에 커서가 있다
프로잭션 화살 내리면서 시작한다 즉 10로우 id10 title 10 그 다음 꺼내려고 하니까 select시작함 뽑아내니 3건이네? ,로 넣을 수 없어서
select id, title , select count(id) from reply_tb where board_id =5
from board_tb;
결과가
커서(초기)
→(내리면) 10 제목10
9 제목 9
8 제목 8
select id, title , select count(id) from reply_tb where board_id =bt.id
from board_tb bt;
컬럼이름 별로임

select id, title , (select count(id) from reply_tb where board_id =bt.id) count
from board_tb bt;

일단 뽑았으니
보드쿼리리포지토리 이동
public void selectV1() {
//게시글마다의 댓글 개수 뽑는 쿼리
//데이터 보면
String sql = """
select id, title , (select count(id) from reply_tb where board_id =bt.id) count
from board_tb bt;
""";
Query query = em.createNativeQuery(sql);
}
테스트가서 보드쿼리리파지토리클래스 만들고 테스트
package org.example.springv3.board;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;
@DataJpaTest
@Import(BoardQueryRepository.class)
public class BoardQueryRepositoryTest {
@Autowired
private BoardQueryRepository boardQueryRepository;
@Test
public void selectV1_test(){
//given
//when
boardQueryRepository.selectV1();
//eye
}
}
이상 없음
보드쿼리레파지토리 이동
이제 값을 볼거임
디버그 모드로 왼쪽 클릭해서 보기

인터넷 mvnRepository
qlrm 치기
jquery를



화면으로 전달하는 dto responseDTO
dataTransfoObject
컨트롤러와 유저 사이에 있다
DB와 레파지토리 사이에 있다 DTO라고 부르기 애매하고 (DB에서 가지고 오는 것 DB데이터 같이)
DB데이터 그래대로 담은것을 model
왜 model인가? 클래스고 테이블인데
db 테이블을 자바세상 클래스로 바꾸는 것을 모델링이라고 한다
설계도 그대로 실제 세상에 올린다 즉 자기세상데이터로 옮기는 것
그래서 model이라고 한다
이거는 우리가 하는게 아니고 ORM이 해준다 (JPA에서는)
컨트롤러가 유저한테 줄 때 DTO
model에서 DTO를 받은건데 아직
DB데이터 아무 변형 없이 줄 수 있어서 그냥 DTO라고 부르기도 한다
한방에 주는 것을 한방쿼리라고 한다 (이런 것을 서비스쿼리도 없다)
보드폴더에 BoardListVO클래스 만들기
나중에는 다른데 옮길건데 일단은 다르게만들어보자
package org.example.springv3.board;
import lombok.Data;
@Data
public class BoardListVO {
private Integer id;
private String title;
private Integer count;
}
보드쿼리레파지토리
vo같은 것 만든다

1건이면 uniqueRest
2건이상이면 list
package org.example.springv3.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import lombok.RequiredArgsConstructor;
import org.qlrm.mapper.JpaResultMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@RequiredArgsConstructor
@Repository
public class BoardQueryRepository {
private final EntityManager em;
public List<BoardListVO> selectV1() {
//게시글마다의 댓글 개수 뽑는 쿼리
//데이터 보면
String sql = """
select id, title , (select count(id) from reply_tb where board_id =bt.id) count
from board_tb bt;
""";
Query query = em.createNativeQuery(sql);
JpaResultMapper mapper = new JpaResultMapper();
List<BoardListVO> boardList = mapper.list(query, BoardListVO.class);
return boardList;
}
}
테스트
package org.example.springv3.board;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;
import java.util.List;
@DataJpaTest
@Import(BoardQueryRepository.class)
public class BoardQueryRepositoryTest {
@Autowired
private BoardQueryRepository boardQueryRepository;
@Test
public void selectV1_test(){
//given
//when
List<BoardListVO> boardList = boardQueryRepository.selectV1();
//eye
// System.out.println(boardList);
}
}
터짐

풀 생성자를 때림
우리는 풀 생성자가 없다 리플랙션으로 구현되지 않았다!
리플랙션으로 구현됐으면 setter로 필요한것 딱딱 넣을 줄 것임
풀 생성자 만들면 될까? 아니
타입이 다르잖아 count는 long이니까
바꾸자
주의점
- 풀 생성자 만들어야 한다
- 타입 맞춰야 한다
보드리스폰스로 이동
package org.example.springv3.board;
import lombok.Data;
import org.example.springv3.reply.Reply;
import org.example.springv3.user.User;
import org.springframework.data.domain.Page;
import java.util.ArrayList;
import java.util.List;
public class BoardResponse {
@Data
public static class DetailDTO {
private Integer id;
private String title;
private String content;
private Boolean isOwner;
// private Integer userId;
private String username;
//리플라이 엔티티 집이 넣으면 안된다 레이지 로딩 되니까! 똑같이 생긴 DTO만들면 된다 비영속객체 만들어서 응답하게 하는게 좋다!
private List<ReplyDTO> replies = new ArrayList<>();
public DetailDTO(Board board, User sessionUser) {
this.id = board.getId();
this.title = board.getTitle();
this.content = board.getContent();
this.isOwner = false;
if (sessionUser != null) {
if (board.getUser().getId() == sessionUser.getId()) {
isOwner = true; // 권한체크
}
}
// this.userId = board.getUser().getId();
this.username = board.getUser().getUsername();
for(Reply reply : board.getReplies()) {
replies.add(new ReplyDTO(reply,sessionUser));
}
}
}
@Data
public static class ReplyDTO{
private Integer id;
private String comment;
private String username;
private Boolean isOwner;
public ReplyDTO(Reply reply, User sessionUser) {
this.id = reply.getId();
this.comment = reply.getComment();
this.username = reply.getUser().getUsername();
this.isOwner = false;
if (sessionUser != null) {
if (reply.getUser().getId() == sessionUser.getId()) {
isOwner = true; // 권한체크
}
}
}
}
@Data
public static class DTO {
private Integer id;
private String title;
private String content;
public DTO(Board board) {
this.id = board.getId();
this.title = board.getTitle();
this.content = board.getContent();
}
}
@Data
public static class PageDTO {
private Integer number; // 현재페이지
private Integer totalPage; // 전체페이지 개수
private Integer size; // 한페이지에 아이템 개수
private Boolean first;
private Boolean last;
private Integer prev; // 현재페이지 -1
private Integer next; // 현재페이지 +1
private List<Content> contents = new ArrayList<>();
private List<Integer> numbers = new ArrayList<>();
public PageDTO(Page<Board> boardPage) {
this.number = boardPage.getNumber();
this.totalPage = boardPage.getTotalPages();
this.size = boardPage.getSize();
this.first = boardPage.isFirst();
this.last = boardPage.isLast();
this.prev = boardPage.getNumber()-1;
this.next = boardPage.getNumber()+1;
int temp = (number / 3)*3; // 0-> 0, 3->3, 6->6
for(int i = temp; i<temp+2; i++){
this.numbers.add(i);
}
//for로 id title만 있으면 확인이 가능하다
//어디서 값을 가지고 와야 하지?
for(Board board : boardPage.getContent()) {
contents.add(new Content(board));
}
}
//생성자를 만들어서
@Data
class Content {
private Integer id;
private String title;
public Content(Board board) {
this.id = board.getId();
this.title = board.getTitle();
}
}
}
@Data // getter, setter, toString
public static class ListDTO {
private Integer id;
private String title;
private Long count;
public ListDTO(Integer id, String title, Long count) {
this.id = id;
this.title = title;
this.count = count;
}
}
}
보트쿼리리포지토리
package org.example.springv3.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import lombok.RequiredArgsConstructor;
import org.qlrm.mapper.JpaResultMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@RequiredArgsConstructor
@Repository
public class BoardQueryRepository {
private final EntityManager em;
public List<BoardResponse.ListDTO> selectV1() {
String sql = """
select id, title, (select count(id) from reply_tb where board_id = bt.id) count
from board_tb bt;
""";
Query query = em.createNativeQuery(sql);
JpaResultMapper mapper = new JpaResultMapper();
List<BoardResponse.ListDTO> boardList = mapper.list(query, BoardResponse.ListDTO.class);
return boardList;
}
}
기존 VO는 지우기
기존 VO
package org.example.springv3.board;
import lombok.Data;
@Data
public class BoardListVO {
private Integer id;
private String title;
private Long count;
public BoardListVO(Integer id, String title, Long count) {
this.id = id;
this.title = title;
this.count = count;
}
}
바뀐 test
package org.example.springv3.board;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;
import java.util.List;
@DataJpaTest
@Import(BoardQueryRepository.class)
public class BoardQueryRepositoryTest {
@Autowired
private BoardQueryRepository boardQueryRepository;
@Test
public void selectV1_test(){
//given
//when
List<BoardResponse.ListDTO> boardList = boardQueryRepository.selectV1();
//eye
System.out.println(boardList);
}
}
Share article