스프링 부트와 JPA 활용1 - 웹 계층 개발 7 & 준영속 엔티티

JOY·2022년 5월 14일
0
post-thumbnail

📌 스프링 부트와 JPA 활용1 - 웹 계층 개발 7 & 준영속 엔티티

인프런 - 스프링 부트와 JPA 활용1 by 김영한 을 기반으로 작성된 글입니다.
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발


웹 계층 개발 목차

1. 홈 화면과 레이아웃

2. 회원 등록

3. 회원 목록 조회

4. 상품 등록

5. 상품 목록

6. 상품 수정

7. 변경 감지와 병합(merge)

8. 상품 주문

9. 주문 목록 검색, 취소


🔎 변경 감지와 병합(merge)

준영속 엔티티

영속성 컨텍스트가 더는 관리하지 않는 엔티티

준영속 엔티티 수정 방법 2가지

변경 감지 기능

영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터 수정할 때 사용

  • 동작방식
    • 트랜잭션 안에서 엔티티 조회, 변경 값 선택
    • 트랜잭션 commit 시점에 변경 감지 기능 동작 해서 DB에 update

병합 merge

준영속 상태의 엔티티를 영속 상태로 변경할 때 사용
준영속 엔티티는 데이터를 변경 해도 DB에 update가 되지 않는다.

  • 동작방식
    • 준영속 엔티티의 식별자 값으로 영속 엔티티 조회
    • 영속 엔티티의 값을 준영속 엔티티 값으로 모두 교체(merge)
    • 트랜잭션 커밋 시점에 변경 감지 기능 동작 해서 DB에 update

💡 이 강의에서 사용하는 병합(merge)은 준영속 엔티티를 수정할 때 사용한다.

DB에 한번 저장 되어 식별자가 존재하는 엔티티는
다시 객체를 생성해도 준영속 엔티티라고 볼 수 있다.

  1. merge() 실행.
  2. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회
    2-1. 만약 1차 캐시에 엔티티가 없으면 데이터베이스에서 엔티티를 조회하고, 1차 캐시에 저장
  3. 조회한 영속 엔티티( mergeMember )에 member 엔티티의 값을 채워 넣음.
    (member 엔티티의 모든 값을 mergeMember에 밀어 넣는다. 이때 mergeMember의 “회원1”이라는 이름이 “회원명변경”으로 바뀐다.)
  4. 영속 상태인 mergeMember를 반환

참고 : 책 자바 ORM 표준 JPA 프로그래밍 3.6.5

💻 참고 코드

@Transactional
public Item updateItem(Long itemId, Book param){

//itemId을 기반으로 실제 DB에 있는 영속성 엔티티를 찾아옴
Item findItem = itemRepository.findOne(itemId);
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());

return findItem;

📍 설명

itemRepository.save(), EntityManager의 persist, merge 모두 호출 안해도 된다

findItem 은 영속상태 값을 세팅 한 후 @Transactional 에 의해 commit 되어
JPA가 트랜잭션 commit 시점에 변경된 데이터를 DB 에 반영한다

ItemRepositoy.java - 병합 merge

상품 엔티티 item은 식별자가 있으므로 merge를 통해 준영속 엔티티로 변경되지 않는다

@Repository
@RequiredArgsConstructor
public class ItemRepository {

    private final EntityManager em;
    
    public void save(Item item){
        if (item.getId() == null){
            em.persist(item);
        } else{
            em.merge(item);
        }
    }

📍 설명

  • save()
    식별자 값이 없으면 새로운 엔티티로 판단하여 영속화(persist)
    식별자 값이 있으면 영속화 되었던 준영속 엔티티로 판단하여 병합(merge)

❗ 주의
변경 감지 기능 사용 - 원하는 속성만 선택해서 변경 가능
병합 사용 - 모든 속성이 변경 (모든 필드 교체)

👉 병합 시 값이 없으면 'null' 로 업데이트 할 위험이 있다
예시) 다른 정보는 변경이 가능 하나 '가격'은 바꿀 수 없도록 지정
이 상태에서 merge를 호출 하면 가격은 null로 update 된다(book.setPrice() 값 세팅 X)

💡 엔티티를 변경할 때는 항상 원하는 데이터만 변경하도록 변경 감지 기능을 사용해라

  • 트랜잭션이 있는 서비스 계층에 식별자와 변경할 데이터를 명확히 전달 해라
  • 트랜잭션이 있는 서비스 계층에서 영속 상태 엔티티를 조회하고 데이터를 직접 변경해라
  • 트랜잭션 커밋 시점에 변경감지가 실행된다.

💻 기존 코드

ItemController.java

@Controller
@RequiredArgsConstructor
public class ItemController {

  @PostMapping(value = "/items/{itemId}/edit")
  public String updateItem(@PathVariable String itemId, @ModelAttribute("form") BookForm form) {

      Book book = new Book();

      book.setId(form.getId());
      book.setName(form.getName());
      book.setPrice(form.getPrice());
      book.setStockQuantity(form.getStockQuantity());
      book.setAuthor(form.getAuthor());
      book.setIsbn(form.getIsbn());

      itemService.saveItem(book);
      return "redirect:/items";
  }
  
}

💻 변경 감지 기능 사용 코드
엔티티를 파라미터로 사용하지 않고 필요한 데이터만 사용 - 유지보수성 향상

ItemService.java


public class ItemService {

    @Transactional
    public void updateItem(Long itemId, String name, int price, int stockQuantity){

		//itemId을 기반으로 실제 DB에 있는 영속성 엔티티를 찾아옴
        Item findItem = itemRepository.findOne(itemId);
        findItem.setName(name);
        findItem.setPrice(price);
        findItem.setStockQuantity(stockQuantity);
    }
    
}

ItemController.java

@Controller
@RequiredArgsConstructor
public class ItemController {

  @PostMapping(value = "/items/{itemId}/edit")
  public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
  	itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());

	return "redirect:/items";
  }
  
}
profile
Just Do IT ------- 🏃‍♀️

0개의 댓글