
default_batch_fetch_size로 쉽게 쓸 수 있지만 동적 쿼리로 직접 만들어보자!
목록보기를 할 때, 게시글과 게시글을 쓴 사람은 다르잖아
게시글은 4갠데 ssar이 게시글을 2개 써서 작성자는 3명. (크기 4, 크기 3)
심지어 게시글은 desc로 되어있어서 최신순부터 나옴.
이걸 작성자랑 게시글을 동적 쿼리, in쿼리 이런 걸 사용해서 맞춰줘야 함
레파지토리에서 바로 짜지 말고, 하나씩 기능 빼서 해보자. 나눠서 차근차근
[ distinct 없다 ]
@Test
public void findAll_custom_inquery_test() {
List<Board> boardList = boardRepository.findAll();
int[] userIds = boardList.stream().mapToInt(board -> board.getUser().getId()).toArray();
for (int i : userIds) {
System.out.println(i);
}
}

user_id를 일단 배열로 담음
3, 2, 1, 1 ?? → 1은 한 번만 들어가야 하니까 distinct를 건다.
[ distinct 사용 ] - toArray로 받기
@Test
public void findAll_custom_inquery_test() {
List<Board> boardList = boardRepository.findAll();
int[] userIds = boardList.stream().mapToInt(board -> board.getUser().getId()).distinct().toArray();
for (int i : userIds) {
System.out.println(i);
}
}

이렇게! 이런 식으로 나와야 함!
그 이후 Repository에서 동일한 것끼리 filter를 써서 매핑 시켜야함.
[ distinct 사용 ] - toList로 받기 (boxed 필요) ← 애가 좋아보임
@Test
public void findAll_custom_inquery2_test() {
List<Board> boardList = boardRepository.findAll();
List<Integer> userIds = boardList.stream().mapToInt(board ->
board.getUser().getId()).distinct().boxed().toList();
System.out.println(userIds);
}

[ 설명 ]
게시판(Board) 엔티티에 연결된 사용자(User)의 ID를 조회하는 것
stream API를 사용하여
boardList의 각 Board 객체에서 사용자(User)의 ID를 추출하여 정수 배열 userIds에 저장한다.
mapToInt는 객체 스트림을 기본형 int 스트림으로 변환하는 중간 연산이며,
board -> board.getUser().getId()는 각 게시판 엔티티에서 사용자 엔티티를 가져와
그 ID를 추출하는 람다 표현식.
마지막으로, toArray() 메서드는 스트림을 배열로 변환
쿼리문을 만들어야함. ? 개수를 정해주는 것! id가 몇 개 들어올지 모르니까!
@Test
public void randomquery_test(){
int[] ids = {1,2};
// select u from User u where u.id in (?,?);
String q = "select u from User u where u.id in (";
for (int i=0; i<ids.length; i++){
//0부터 시작해서 -1을 해줌. 쿼리문 마지막에는 )를 닫아야하니까, 마지막에 )를 해줌
if(i == ids.length -1){
q = q + "?)";
}else{
q = q + "?,";
}
}
System.out.println(q);
}

? 쓰면 안된다. [ :id ] 이거 써야한다. JPQL이니까! → 고쳐야 한다!!
일단 이런 식으로 나와야 한다~ 정도만 생각하자!
JPQL과 List로 쿼리문을 만들어주자!! (동적으로 쿼리 문자열 생성!)
@Test
public void randomquery_test(){
// int[] ids = {1, 2, 3, 4};
List<Integer> ids = Arrays.asList(1, 2, 3, 4);
// select u from User u where u.id in (?,?);
String q = "select u from User u where u.id in (";
//User 테이블에서 id가 다음 리스트(ids) 안에 있는 데이터를 모두 골라줘
for (int i=0; i<ids.size(); i++){
//0부터 시작해서 -1을 해줌. 쿼리문 마지막에는 )를 닫아야하니까, 마지막에 )를 해줌
//사이즈는 4인데, 배열은 0부터 시작하니 0, 1, 2, 3. 때문에 마지막을 나타내려면 -1 해줌!
if(i == ids.size() -1){
q = q + ":id" + i + ")";
}else{
q = q + ":id" + i + ",";
}
}
System.out.println(q);
}
User라는 데이터 테이블에서, 특정 id값을 가진 데이터를 찾기 위한 명령문(쿼리)을 만드는 것
우리가 찾고 싶은 id 값들은 1, 2, 3, 4
리스트(ids)에 있는 각 숫자를 하나씩 살펴보면서, 쿼리에 추가.
숫자를 추가할 때는 ":id0", ":id1"처럼 ":id" 뒤에 숫자를 붙여서 표시한다.
이렇게 하면 나중에 실제 숫자로 바꿔 넣을 수 있다!
최종적으로 "select u from User u where u.id in (:id0,:id1,:id2,:id3)"라는
쿼리를 만드는 게 목표!
즉, 나는 1, 2, 3, 4라는 id를 가진 User 데이터를 찾고 싶어"라고
데이터베이스에 말하는 방법을 만드는 것!

이제 값을 넣을 때
query.setAttribute(”id0”, id0);
query.setAttribute(”id1”, id1);
query.setAttribute(”id2”, id2);
이렇게 넣어줄 것 → for문 돌려서 넣기. 이건 3개일 때만 사용할 수 있어서 재사용을 못하니까 늘어날 경우를 대비해 for문으로 !! 해야함!!
BoardRepository → 미완
public class BoardRepository {
private final EntityManager em;
public List<Board> findAllV2() {
String q1 = "select b from Board b order by b.id desc";
//lazy로딩하면 망한다. 인쿼리(동적쿼리)로 만들어야 함.
List<Board> boardList = em.createQuery(q1, Board.class).getResultList(); //애 크기 4
String q2 = ""; //boardList가 가지고 있는 유저의 개수만큼 ?를 걸어서 in 쿼리가 나와야함.
List<User> userList = em.createQuery(q2, User.class).getResultList(); //애 크기 3
return boardList; //리턴되는 boardList에는 user가 채워져 있어야 함
//stream의 filter 사용 -> 다들 map을 사용함
}
정답
https://getinthere.notion.site/4-2-in-query-12863cbf332d4b6a9e0305cf264b3393

보드가 들고 있는 번호를 다 뽑아낸다.
stream 안 쓰면 코드가 복잡... (for문으로 돌려야함ㅠㅠ)
map으로 가공하면 자기 자신을 리턴하는데, mapToInt로 돌려준다.
그럼 이제 물길에 int로 바뀌어서 돌려준 것.
1, 1, 2, 3 라고 중복이 나오니까 distict 해서 1, 2, 3으로 바꿔줌
boxed라는 문법으로 받아야지 toList로 바꿔줌. (boxed.toList)
아니면 toArry라고 배열로 받아줘야함.

stream 안 썼을 땐 이렇게 for문을 돌림….. (중복 제거해주려고 Set 씀)
이제 쿼리문 살펴보자

:userIds 하면 자기가 알아서 막 집어넣어줌.
[ 1, 2, 3 ] 이 들어가겠지.
id를 서로 비교해서 같으면 setUser해서 넣어주면
정수씨 버전
테스트로 확인



user_id가 정확하게 매칭이 된 것도 확인
쌤 블로그 정리본
1. 조회 전략
우선 Lazy 전략으로 Board만 조회하고,
Lazy loading을 통해 user들을 in query로 조회한다. (쿼리 2번만 끝)
Hibernate:
select
b1_0.id,
b1_0.content,
b1_0.created_at,
b1_0.title,
b1_0.user_id
from
board_tb b1_0
order by
b1_0.id desc
Hibernate:
select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
u1_0.username
from
user_tb u1_0
where
u1_0.id in (?, ?, ?)
2. 조회 쿼리 작성
1. 아래와 같이 조회를 두번한다.
2. 두개의 결과를 2중 for문을 돌면서 filter로 매칭시킨다.
2-1. V2는 JPQL에 List를 직접 매칭시키는 법 ← 어려워!
public List<Board> findAllV2(){
Query q1 = em.createQuery("select b from Board b order by b.id desc", Board.class);
List<Board> boardList = q1.getResultList();
List<Integer> userIds = boardList.stream().mapToInt(value -> value.getUser().getId()).distinct().boxed().toList();
Query q2 = em.createQuery("select u from User u where u.id in :userIds", User.class);
q2.setParameter("userIds", userIds);
List<User> userList = q2.getResultList();
boardList.stream().forEach(b -> {
User user = userList.stream().filter(u -> u.getId() == b.getUser().getId()).findFirst().get();
b.setUser(user);
});
return boardList;
}
2-2. V3는 for문을 돌면서 동적 쿼리를 만들어 내는 법 ← 기본기!
public List<Board> findAllV3(){
String q1 = "select b from Board b order by b.id desc";
List<Board> boardList = em.createQuery(q1 , Board.class).getResultList();
int[] userIds = boardList.stream().mapToInt(board -> board.getUser().getId()).distinct().toArray();
String q2 = "select u from User u where u.id in (";
for (int i = 0; i < userIds.length ; i++) {
if (i == userIds.length - 1){
q2 = q2 + userIds[i] + ")";
}else {
q2 = q2 + userIds[i] + ",";
}
}
List<User> userList = em.createQuery(q2 , User.class).getResultList();
for (Board board : boardList){
for (int i = 0; i < userList.size(); i++) {
User user = userList.get(i);
if (user.getId() == board.getUser().getId()){
board.setUser(user);
}
}
}
return boardList; // user가 채워져 있어야함.
}
3. 조회 쿼리 테스트
@Test
public void findAllV2_test(){
List<Board> boardList = boardRepository.findAllV2();
System.out.println("findAllV2_test : 조회완료 쿼리 2번");
boardList.forEach(board -> {
System.out.println(board);
});
}

Share article