
1. in쿼리
- 데이터베이스 질의 언어(SQL)에서 사용되는 용어 중 하나
- 특정한 조건을 충족하는 여러 값을 검색할 때 사용
SELECT * FROM 테이블명 WHERE 열명 IN (값1, 값2, 값3, ...);
in 뒤에 오는 값들은 특정 열의 값과 비교
여러 값 중 하나라도 조건을 만족하는 경우 해당 행을 결과로 반환
여러 개의 OR 조건을 하나의 쿼리로 간단하게 작성할 수 있도록 도와줌
- 특히 여러 개의 가능한 값을 가진 조건에 유용하게 사용됩니다.
2. BoardRepository 에 findAll() 만들기
- 스칼라 쿼리 : select(r) from Reply r order
package shop.mtcoding.blog.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.List;
@RequiredArgsConstructor
@Repository
public class BoardRepository {
private final EntityManager em;
public List<Board> findAll() { // 다른 정보가 추가로 필요하면 조인하면 됨 - outer join일 때 outer는 생략 가능
Query query = em.createQuery("select b from Board b order by b.id desc", Board.class);
return query.getResultList();
}
public Board findByIdJoinUser(int id) { // on생략 가능, 알아서 pk연결해줌
Query query = em.createQuery("select b from Board b join fetch b.user u where b.id = :id", Board.class);
query.setParameter("id", id);
return (Board) query.getSingleResult();
}
public Board findById(int id) {
//id, title, content, user_id(이질감), created_at
Board board = em.find(Board.class, id);
return board;
}
}
- 단위 테스트하기
package shop.mtcoding.blog.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 shop.mtcoding.blog.board.Board;
import shop.mtcoding.blog.board.BoardRepository;
@Import(BoardRepository.class)
@DataJpaTest
public class BoardRepositoryTest {
@Autowired
private BoardRepository boardRepository;
@Test
public void findAll_test(){
// given
// when
boardRepository.findAll();
// then
}
@Test
public void findByIdJoinUser_test() {
int id = 1;
boardRepository.findByIdJoinUser(id);
}
@Test
public void findById_test() {
int id = 1;
System.out.println("start - 1");
Board board = boardRepository.findById(id);
System.out.println("start - 2");
System.out.println(board.getUser().getId());
System.out.println("start - 3");
System.out.println(board.getUser().getUsername());
}
}

- 다른 데이터의 Eager
- 다른 데이터의 Lazy



- 동일한 데이터의 Eager
- 동일한 데이터의 Lazy


Eager 전략일 때
findById 일때는 조인
findAll 일때는 Board만 조회한 뒤에 User가 게시글을 쓴 수만큼 select 절이 더 발생
- LazingLoading
package shop.mtcoding.blog.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 shop.mtcoding.blog.board.Board;
import shop.mtcoding.blog.board.BoardRepository;
import java.util.List;
@Import(BoardRepository.class)
@DataJpaTest
public class BoardRepositoryTest {
@Autowired
private BoardRepository boardRepository;
@Test
public void findAll_test() {
// given
// when
List<Board> boardList = boardRepository.findAll();
boardList.forEach(board -> {
System.out.println(board.getUser().getUsername());
});
// then
}
@Test
public void findByIdJoinUser_test() {
int id = 1;
boardRepository.findByIdJoinUser(id);
}
@Test
public void findById_test() {
int id = 1;
System.out.println("start - 1");
Board board = boardRepository.findById(id);
System.out.println("start - 2");
System.out.println(board.getUser().getId());
System.out.println("start - 3");
System.out.println(board.getUser().getUsername());
}
}


- default_batch_fetch_size
복잡한 조회쿼리 작성시 지연로딩으로 발생해야 하는 쿼리를 IN절로 한번에 모아보내는 기능
server:
servlet:
encoding:
charset: utf-8
force: true
spring:
mustache:
servlet:
expose-session-attributes: true
expose-request-attributes: true # Model을 쓰면 안써도 됨
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:test;MODE=MySQL
username: sa
password:
h2:
console:
enabled: true
sql:
init:
data-locations:
- classpath:db/data.sql
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 10
defer-datasource-initialization: true

2. BoardController 에 index 수정하기
package shop.mtcoding.blog.board;
import ch.qos.logback.core.model.Model;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.lang.annotation.Native;
import java.util.List;
@RequiredArgsConstructor
@Controller
public class BoardController {
private final BoardRepository boardRepository;
// @Transactional 트랜잭션 시간이 너무 길어져서 service에 넣어야함
@PostMapping("/board/{id}/update")
public String update(@PathVariable Integer id) {
return "redirect:/board/" + id;
}
@GetMapping("/board/{id}/update-form")
public String updateForm(@PathVariable(name = "id") Integer id, HttpServletRequest request) {
return "/board/update-form"; // 서버가 내부적으로 index를 요청 - 외부에서는 다이렉트 접근이 안됨
}
@PostMapping("/board/{id}/delete")
public String delete(@PathVariable Integer id) { // DTO 없이 구현
return "redirect:/";
}
@GetMapping("/")
public String index(HttpServletRequest request) {
List<Board> boardList = boardRepository.findAll();
request.setAttribute("boardList", boardList);
return "index"; // 서버가 내부적으로 index를 요청 - 외부에서는 다이렉트 접근이 안됨
}
@PostMapping("/board/save")
public String save() { // DTO 없이 구현
return "redirect:/";
}
@GetMapping("/board/save-form")
public String saveForm() {
return "board/save-form";
}
@GetMapping("/board/{id}")
public String detail(@PathVariable Integer id, HttpServletRequest request) { // Integer : 없으면 null, int : 0
Board board = boardRepository.findByIdJoinUser(id);
request.setAttribute("board", board);
return "board/detail";
}
}
{{> /layout/header}}
<div class="container p-5">
{{#boardList}} <!--for문 돌리기-->
<div class="card mb-3">
<div class="card-body">
<h4 class="card-title mb-3">{{title}}</h4>
<div class="mb-3">{{boardDate}}</div> <!--날짜 시간 추가하기-->
<a href="/board/{{id}}" class="btn btn-primary">상세보기</a>
</div>
</div>
{{/boardList}}
<ul class="pagination d-flex justify-content-center">
<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</div>
{{> /layout/footer}}

3. in쿼리 사용하기
package shop.mtcoding.blog.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 shop.mtcoding.blog.board.Board;
import shop.mtcoding.blog.board.BoardRepository;
import java.util.List;
@Import(BoardRepository.class)
@DataJpaTest
public class BoardRepositoryTest {
@Autowired
private BoardRepository boardRepository;
@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);
}
// select * from user_tb where id in (3,2,1,1);
// select * from user_tb where id in (3,2,1);
}
}

package shop.mtcoding.blog.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 shop.mtcoding.blog.board.Board;
import shop.mtcoding.blog.board.BoardRepository;
import java.util.List;
@Import(BoardRepository.class)
@DataJpaTest
public class BoardRepositoryTest {
@Autowired
private BoardRepository boardRepository;
@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++){
if(i==ids.length-1){
q = q + "?)";
}else{
q = q + "?,";
}
}
System.out.println(q);
}
@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);
}
}
}

@Test
public void findAll_inquery_test() {
// 보드에 해당하는 모든 사용자 ID 조회
String q1 = "SELECT DISTINCT b.user.id FROM Board b";
List<Integer> userIds = em.createQuery(q1, Integer.class).getResultList();
// 각 사용자 ID에 해당하는 보드를 찾아서 사용자 할당
for (Integer userId : userIds) {
// 해당 사용자 ID에 해당하는 모든 보드를 찾기
String q2 = "SELECT b FROM Board b WHERE b.user.id = :userId";
List<Board> boards = em.createQuery(q2, Board.class)
.setParameter("userId", userId)
.getResultList();
// 해당 사용자 ID에 해당하는 사용자를 찾기
User user = em.find(User.class, userId);
// 각 보드에 사용자 할당 및 결과 출력
for (Board board : boards) {
board.setUser(user);
System.out.println("Board ID: " + board.getId() + ", User ID: " + user.getId());
}
}
}


@Test
public void findAll_inqueryv2_test() {
String q = "SELECT DISTINCT b FROM Board b JOIN FETCH b.user";
List<Board> boards = em.createQuery(q, Board.class).getResultList();
for (Board board : boards) {
System.out.println("Board ID: " + board.getId());
System.out.println("User ID: " + board.getUser().getId());
// 사용자(User) 객체에 대한 다른 정보도 필요하다면 추가 가능
}
}



Share article