
Contents
리팩토링하기1. JPA
- Java 애플리케이션에서 데이터베이스와의 상호 작용을 관리하는 Java 표준 스펙
- Hibernate와 동일하다 생각해도 됨
JPA를 구현한 가장 널리 사용되는 ORM 프레임워크 중 하나
- 비영속 객체 : 아직 데이터베이스에 저장되지 않은 객체
영속성 컨텍스트에 저장되지 않은 상태
- 객체를 Persistence Context에 저장하여 관리

2. Persistence Context
- JPA가 데이터베이스와 상호 작용하는 데 사용하는 환경
- 모든 문맥을 다 알고 있음
- 영속 객체 : 영속성 컨텍스트에 저장, 데이터베이스와의 동기화 됨
비영속 객체 : 아직 HD(DB)에 저장되지 않음, PK가 없음
- 영속 엔티티를 만들 때는 보통 DB의 PK 값을 사용하여 객체를 식별
- 객체를 처음으로 영속성 컨텍스트에 저장할 때는 DB에 저장된 PK 값이 없음
- Hibernate와 같은 ORM 프레임워크가 새로운 PK 값을 생성하고 객체에 할당
객체 값을 만들어서 insert쿼리로 DB에 집어 넣음
- DB에 저장, PK 값이 생성되면 영속성 컨텍스트와 동기화되어 영속 객체로서의 상태를 유지
insert가 되고 PK가 생기면 PC가 동기화 되면서 영속성 객체로 바뀜
리팩토링하기
1. Board 수정하기
- 빈 생성자가 디폴트
- 변경하는 데이터를 PC에 담기 위해 해당 데이터의 생성자 만들기
package shop.mtcoding.blog.board;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import shop.mtcoding.blog.util.MyDateUtil;
import java.sql.Timestamp;
@NoArgsConstructor // 빈생성자 만들어주기
@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;
@CreationTimestamp // PC로 인해 DB에 INSERT될 때 날짜 주입
private Timestamp createdAt;
public Board(String title, String content, String username) {
this.title = title;
this.content = content;
this.username = username;
}
public String getBoardDate(){
return MyDateUtil.timestampFormat(createdAt);
}
}
2. BoardPersistRepository 만들어 save() 수정하기
- PC에 Data(주소) 넣기 / Entity만 가능함
- 복잡한 통계들은 Native Query 사용
package shop.mtcoding.blog.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.List;
@RequiredArgsConstructo
@Repository
public class BoardPersistRepository {
private final EntityManager em;
@Transactional
public Board save(String title, String content, String username) {
// 보드 객체 만들기
Board board = new Board(title, content, username);
// PC에 Data 주소 넣기(Entity만 가능함)
em.persist(board);
// 실행후 영속 객체가 됨
return board;
}
}
- save() 단위 테스트하기
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.BoardNativeRepository;
import shop.mtcoding.blog.board.BoardPersistRepository;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@Import(BoardNativeRepository.class)
@DataJpaTest
public class boardPersistRepositoryTest {
@Autowired // DI
private BoardPersistRepository boardPersistRepository;
@Test
public void save_test() {
// given
String title = "제목5";
String content = "내용5";
String username = "ssar";
// when
Board board = boardPersistRepository.save(title, content, username);
System.out.println("save_test : " + board);
// then
}
}

3. BoardRequest 만들어서 SaveDTO() 만들기
- 비영속 객체를 PC에 넣기 위해 insert 때만 Entity로 바꾸는 메서드가 필요함
package shop.mtcoding.blog.board;
import lombok.Data;
public class BoardRequest {
@Data
public static class SaveDTO {
private String title;
private String content;
private String username;
// Entity로 바꾸는 메서드 - 비영속 객체를 PC에 넣기 위해 insert 때만 필요함
public Board toEntity(){
return new Board(title, content, username);
}
}
}
- 수정하기
매개변수 Board객체 집어넣기
package shop.mtcoding.blog.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.Lis
@RequiredArgsConstructor
@Repository
public class BoardPersistRepository {
private final EntityManager em;
@Transactional
public Board save(Board board) {
// PC에 Data 주소 넣기(Entity만 가능함)
em.persist(board);
// 실행후 영속 객체가 됨
return board;
}
}
- 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.BoardPersistRepository;
@Import(BoardPersistRepository.class)
@DataJpaTest
public class BoardPersistRepositoryTest {
@Autowired //DI
private BoardPersistRepository boardPersistRepository;
@Test
public void save_test() {
//given
Board board = new Board("제목5","내용5","ssar");
//when
boardPersistRepository.save(board);
//then
System.out.println(board);
}
}

4. BoardController 에 save() 수정하기
package shop.mtcoding.blog.board;
import ch.qos.logback.core.model.Model;
import jakarta.servlet.http.HttpServletRequest;
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 BoardNativeRepository boardNativeRepository;
private final BoardPersistRepository boardPersistRepository;
@PostMapping("/board/{id}/update")
public String update(@PathVariable Integer id, String title, String content, String username){
boardNativeRepository.updateById(id, title, content, username);
return "redirect:/board/"+id;
}
@GetMapping("/board/{id}/update-form")
public String updateForm(@PathVariable (name="id") Integer id, HttpServletRequest request) {
Board board = boardNativeRepository.findById(id);
request.setAttribute("board", board);
return "/board/update-form"; // 서버가 내부적으로 index를 요청 - 외부에서는 다이렉트 접근이 안됨
}
@PostMapping("/board/{id}/delete")
public String delete(@PathVariable Integer id) { // DTO 없이 구현
boardNativeRepository.deleteById(id);
return "redirect:/";
}
@GetMapping("/")
public String index(HttpServletRequest request) {
// 조회하기
List<Board> boardList = boardNativeRepository.findAll();
// 가방에 담기
request.setAttribute("boardList", boardList);
return "index"; // 서버가 내부적으로 index를 요청 - 외부에서는 다이렉트 접근이 안됨
}
@PostMapping("/board/save")
public String save(BoardRequest.SaveDTO reqDTO) { // DTO 없이 구현
boardPersistRepository.save(reqDTO.toEntity());
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 = boardNativeRepository.findById(id);
request.setAttribute("board", board);
return "board/detail";
}
}

Share article