14. 게시판 목록보기v3

송민경's avatar
Mar 14, 2024
14. 게시판 목록보기v3

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()); } }
 
notion image
- 다른 데이터의 Eager
- 다른 데이터의 Lazy
notion image
notion image
 
notion image
- 동일한 데이터의 Eager
- 동일한 데이터의 Lazy
notion image
notion image
💡
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()); } }
notion image
notion image
 
  • 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
notion image
 

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}}
notion image
 

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); } }
notion image
 
 
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); } } }
notion image
 
@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()); } } }
notion image
notion image
 
@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) 객체에 대한 다른 정보도 필요하다면 추가 가능 } }
notion image
notion image
 
notion image
Share article

vosw1