13. 게시판 상세보기v3

송민경's avatar
Mar 13, 2024
13. 게시판 상세보기v3

1. 핵심 개념

  • EAGER : 연관된 객체를 다 채워줌 / 조인 발생
EAGER로 설정된 연관된 객체는 부모 객체를 조회할 때 함께 즉시 로딩됨
즉, 부모 객체를 조회할 때 연관된 모든 자식 객체들도 함께 DB에서 조회되어 메모리에 뜸
  • LAZY : 특정 작업이나 처리를 필요한 시점에 지연시키는 방식
  • LAZY loading : ORM에서 연관된 객체를 실제로 사용할 때까지 로딩을 지연시키는 방식
- 상태 : LAZY
- 조건 : 연관된 객체에 PK가 아닌 필드 값에 접근하면 지연 로딩이 발생
- 결과 : 값이 없으니까 AWAIT가 걸려서 조회해서 채워넣어 줌
해당 객체가 없는 경우에는 NULL이 반환
- 장점 : 필요한 경우에만 해당 객체가 로딩되므로 불필요한 데이터베이스 호출을 피할 수 있음
-단점 : 필요한 경우가 많고, 성능에 큰 영향을 주지 않는다면, JOIN이 더 효율적일 수 있음
  • ORM(Object-Relational Mapping) : 객체와 관계형 데이터베이스 간의 매핑을 자동화하는 기술
객체 지향 프로그래밍에서 사용되는 객체와 관계형 데이터베이스 간의 불일치 해결
객체를 DB 테이블에 매핑
객체 간의 관계를 관계형 DB의 관계로 매핑하여 객체 지향적인 프로그래밍을 지원
개발자가 객체 지향적인 방식으로 데이터를 다룰 수 있도록 도와줌
DB와의 상호작용을 추상화하여 생산성을 향상시킴
 

2. 우리가 사용할 전략

  • Lazy 전략을 사용
  • 필요한 경우 직접 조인하자
 

3. BoardRepository 에 findById() 만들기

  • PC에 없어서 DB에서 조회해서 (id, content, user_id, created_at) 가져오려고 시도 함
없어서 못 가져와서 조회를 한번 더 해야 함
  • Board를 조회하면 user_id를 User 객체의 id에 담아서 Board 객체를 만들어 줌
package shop.mtcoding.blog.board; import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @RequiredArgsConstructor @Repository public class BoardRepository { private final EntityManager em; 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 jakarta.persistence.*; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import shop.mtcoding.blog.user.User; import shop.mtcoding.blog.util.MyDateUtil; import java.sql.Timestamp; @Data // 변경되는 데이터에만 setter가 필요함 @Table(name = "board_tb") @Entity public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String title; private String content; // private String username; // username은 user_tb에도 있기에 외래키로 연관관계 맺어야 함 // @JoinColumn(name = "user_id") 직접 지정할 때 @ManyToOne(fetch = FetchType.LAZY) // ORM 할 것이기에 user 객체 필요 private User user; // DB에 컬럼명 : user_id (변수명_PK키) @CreationTimestamp // PC로 인해 DB에 INSERT될 때 날짜 주입 private Timestamp createdAt; public String getBoardDate(){ return MyDateUtil.timestampFormat(createdAt); } }
      notion image
       

4. Board 에 fetch 설정하기

package shop.mtcoding.blog.board; import jakarta.persistence.*; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import shop.mtcoding.blog.user.User; import shop.mtcoding.blog.util.MyDateUtil; import java.sql.Timestamp; @Data // 변경되는 데이터에만 setter가 필요함 @Table(name = "board_tb") @Entity public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String title; private String content; // private String username; // username은 user_tb에도 있기에 외래키로 연관관계 맺어야 함 // @JoinColumn(name = "user_id") 직접 지정할 때 @ManyToOne(fetch = FetchType.LAZY) // ORM 할 것이기에 user 객체 필요 private User user; // DB에 컬럼명 : user_id (변수명_PK키) @CreationTimestamp // PC로 인해 DB에 INSERT될 때 날짜 주입 private Timestamp createdAt; public String getBoardDate(){ return MyDateUtil.timestampFormat(createdAt); } }
  • fetch 전략을 적지 않으면 디폴트가 Eager
  • Eager / join이 일어남
@ManyToOne(fetch = FetchType.EAGER) // ORM 할 것이기에 user 객체 필요 private User user; // DB에 컬럼명 : user_id (변수명_PK키)
  • LAZY : 내껏만 / join이 일어나지 않음
@ManyToOne(fetch = FetchType.LAZY) // ORM 할 것이기에 user 객체 필요 private User user; // DB에 컬럼명 : user_id (변수명_PK키)
 
  • 단위 테스트하기
 

5. BoardController 에detail 수정하기

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) { 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"; } }
notion image
Share article

vosw1