1. 영속성 컨텍스트(Persistence Context) 란?
영속성 컨텍스트(Persistence Context)는 JPA에서 Server와 데이터 베이스 사이에 엔티티를 영구 저장하는 환경이자 논리적인 영역이다.
비영속 객체
메모리상에만 존재하는 엔티티. 아직 데이터베이스에 저장되지 않았거나, 이미 저장되었다가 영속성 컨텍스트에서 분리(detached)된 상태의 엔티티를 의미한다.
영속 객체
데이터베이스에 저장되었거나 저장될 예정인 엔티티로, JPA가 해당 엔티티의 생명주기를 관리한다.
2. PC를 활용한 테이블 INSERT
라이브러리 설치
implementation group: 'org.mindrot', name: 'jbcrypt', version: '0.4' implementation group: 'org.qlrm', name: 'qlrm', version: '4.0.1' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0'
Persistence Context를 활용하기 전 네이티브 쿼리를 활용해 데이터를 INSERT 했다.
BoardNativeRepository
@Transactional public void save(String title,String content, String username){ Query query = em.createNativeQuery("insert into board_tb(title,content,username,created_at) values (?,?,?,now())"); query.setParameter(1,title); query.setParameter(2,content); query.setParameter(3,username); query.executeUpdate(); }
지금부터 Persistence Context 를 활용해본다.
Persistence Context 에 엔티티를 전달할 때 ID는 전달되지 않는다. Board 객체가 PC에 전달되고 DB에 저장되면 ID가 생성되고, 영속 객체가 된다.
3. View 확인
클라이언트에게 username, title, content 세 가지 데이터를 입력받는다. 데이터를 받기 위해 DTO를 생성한다.
import lombok.Data; public class BoardRequest { @Data public static class SaveDTO{ private String title; private String content ; private String username ; public Board toEntity(){ //DTO를 ENTITY 로 바꾸는 메서드 return new Board(title,content,username); } } }
Board 엔티티에서 입력받을 데이터는 title, content, username 이다. PC 에는 엔티티 타입만 전달할 수 있기 때문에
toEntity
메서드를 만들어 세 가지 데이터를 엔티티로 만든다.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 // 빈생성자가 필요 @Entity @Data @Table(name = "board_tb") public class Board { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer id; private String title; private String content; private String username; @CreationTimestamp // persistance centext 에 전달될 때 자동으로 주입됨. private Timestamp createdAt; //INSERT할 데이터를 클라이언트에게 받는다. public Board(String title, String content, String username) { this.title = title; this.content = content; this.username = username; } }
엔티티 클래스에서 변경할 3가지 데이터를 Board 생성자로 만들어 관리한다.
pc에서 DB로 쿼리가 날려질 때 기본생성자가 필요하다. 그래서
@NoArgsConstructor
가 필요하다.4. 컨트롤러
@PostMapping("/board/save") public String save(BoardRequest.SaveDTO requsetDTO){ //DTO 없이 데이터 받음 boardPersistRepository.save(requsetDTO.toEntity()); return "redirect:/"; }
DTO로 받은 데이터를
toEntity
메서드로 전달한다.4. 레파지토리
import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Repository public class BoardPersistRepository { private final EntityManager em ; @Transactional public Board save(Board board){ // DTO로 데이터가 넘어올 때는 비영속 객체 Board boardPC = em.persist(board); // insert. pc에 전달 //데이터가 PC에 전달된 이후에는 영속 객체가 되며 이때 ID가 생성된다. return boardPC } }
PC 를 사용하기 이전에는 INSERT 를 한 후 ID를 조회하려면 MAX(ID)를 조회해야 했다. 하지만 PC 를 사용하면 board 자체가 영속 객체가 되기 때문에
return
board
를 하면 된다.
하지만 헷갈릴 수 있기 때문에 boardPC
를 구분해서 사용했다.5. JUnit 테스트
test/java/shop.mtcoding.blog/board/BoardPersistRepository
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(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); } }
JUnit 테스트가 정상적으로 작동한다. 이제 실제 파일을 실행해본다.
Persistence Context를 사용해 데이터를 INSERT 했다.
Share article