Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

💥변경 감지와 병합(merge)💥 본문

인프런/[인프런] Springboot와 JPA활용 1

💥변경 감지와 병합(merge)💥

Seung__ 2021. 8. 6. 20:22
너~~~~~~~~~~~무 중요

1. 변경감지, dirty checking


  • 일반적으로 Transaction안에서 엔티티가 변경이 되면, 
  • flush가 되는 시점에 dirty checking을 통해 변경된 내용에 대해 update query가 실행된다.
  • 이를 dirty checking이라 한다.
  • 하지만 문제는 영속성 컨텍스트가 관리하지 않는 준영속 엔티티에서 발생한다.
 

Dirtycheck, 성능이슈

@Service public class CommentService { @Autowired private CommentRepository commentRepository; @Transactional public void innit(){ for(int i=0; i comments = commentRepository.findAll(); for(Comment..

bsh-developer.tistory.com

 

2. 준영속 엔티티


  • 영속성 컨텍스트가 더 이상 관리하지 않는 엔티티를 말함.
  • 이전 글에서 itemService.saveItem(book)에서 Book 객체는 수정을 시도한다.
  • Book객체는 이미 DB에 한 번 저장되어서 식별자가 존재함.
  • 이렇게 임의로 만들어낸 엔티티도 기존 식별자를 가지고 있으면 준영속 엔티티이다.
  • 준 영속 엔티티는 변경 감지가 일어나지 않는다.
  • 아예 새로운 엔티티면 persist를 하면 되지만
  • 현재  수정하는 상황은 새로운 객체를 만들어서 기존 엔티티의 id를 넣어주는 상황이다.
  • 해당 객체는 준영속 엔티티이고,
  • 다른 엔티티처럼 tansaction안 에서 변경해도 dirty checking 대상이 아니다.

 

3. 준영속 엔티티를 수정하는 방법


  • 변경 감지 기능 사용(dirtychecking)
  • merge 사용

 

 

4. dirtyChecking


  • 새로운 객체에 영속상태의 엔티티 정보를 넣는 것 이 아니라
  • 영속상태의 객체를 db에서 찾아오고 새로운 정보를 영속상태 엔티티에 넣자.
  • 이렇게 되면 영속상태 엔티티가 변경이 되었기 때문에
  • 변경감지(dirty checking)의 대상이고
  • transaction 종료 시점에 변경감지가 일어나면서 변경된 내용에 대하여 update 쿼리가 실행된다.
    @Transactional
    public void updateItem(Long itemId, Book bookParam){
      Item findItem = itemRepository.findOne(itemId); //영속상태 엔티티 찾아와서
      findItem.setPrice(bookParam.getPrice());        //영속상태 엔티티를 변경함.
      findItem.setName(bookParam.getName());          //영속상태이므로 dirtycheking 대상임.
    
    }​

 

 

5. 병합(merge)


  • 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용.
  • 결국 같은 식별자로 DB에서 엔티티를 찾아서
  • 모든 data를 바꿔치기함. 즉 같은 식별자의 영속화된 엔티티를 불러와서 새로운 정보 싹다 입력
  • 결국 영속화된 엔티티가 변경이됨. 그래서 commit 될 때 반영이 됨.
  • 즉, merge를 하면 JPA가 알아서 아래 코드 돌려주는 거임. 
    @Transactional
    public Item updateItem(Long itemId, Book bookParam){
      Item findItem = itemRepository.findOne(itemId); //영속상태 엔티티 찾아와서
      findItem.setPrice(bookParam.getPrice());        //영속상태 엔티티를 변경함.
      findItem.setName(bookParam.getName());          //영속상태이므로 dirtycheking 대상임.
      return findItem;
    }​​
  • merge 동작 방식
    - merge()실행
    - 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티 조회
      (1차 캐시에 엔티티 없으면 DB에서 엔티티 조회하고, 1차캐시에 저장함 ㅎ)
    - 조회한 영속 엔티티에 새로운 엔티티의 값을 채워 넣는다.
    - 즉 새로운 객체 데이터를 영속 엔티티에 밀어 넣는다.
    - 영속 상태인 엔티티를 반환함.

 

  • 병합은 조심해야함..
    - 변경 감지 기능을 사용하면 원하는 속성만 변경이 가능해..
    - 근데 병합을 사용하면 데이터를 싹 밀어 넣으니 모든 속성이 변경됨!!!!!!!!
    - 만약 병합하는데 값이 없다??? null!!!로 업데이트 됨...ㄷㄷ data 날라감..
  • 가급적 merge를 쓰지 말자
  • 최대한 변경감지를 사용하자.
  • setter도 쓰지 말자.ㅋㅋㅋ

 

 

 

6. 가장 좋은 해결 방법


엔티티를 변경할 경우에 항상 변경감지(Dirty Checking)을 사용하자
  • 컨트롤러에서 애매하게 엔티티 생성하지마 
    - 이렇게 컨트롤러에서 엔티티 생성해서 넘기지 말고
        @GetMapping("items/{itemId}/edit")
        public String updateItemForm(@PathVariable("itemId") Long itemId, Model model){
            Book item = (Book) itemService.findOne(itemId);
    
            //수정할 때 book 엔티티가아니라 book fom을 보낼거임.
            BookForm form  = new BookForm();
            form.setId(item.getId());
            form.setName(item.getName());
            form.setPrice(item.getPrice());
            form.setStockQuantity(item.getStockQuantity());
            form.setAuthor(item.getAuthor());
            form.setIsbn(item.getIsbn());
    
            model.addAttribute("form", form);
            return "items/updateItemForm";
        }​
    - 컨트롤러는 정보만 넘기고
    @PostMapping("items/{itemId}/edit")
    public String updateItem(@ModelAttribute("form") BookForm form, @PathVariable("itemId") Long itemId){
    
      itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());
    
      return "redirect:/items";
    }​
    -  서비스 계층에서 엔티티를 조회하여 엔티티의 값을 변경하자.
@Transactional
public void updateItem(Long itemId, String name, int price, int stockQuantity){
  Item findItem = itemRepository.findOne(itemId);  //영속상태 엔티티 찾아와서
  findItem.setName(name);                          //영속상태 엔티티를 변경함.
  findItem.setStockQuantity(stockQuantity);        //영속상태이므로 dirtycheking 대상임.

}
  • Transaction이 있는 서비스 계층에, 식별자(id)와 변경할 데이터를 명확하게 전달!
    - parameter or DTO
  • Transaction이 있는 서비스 계청에선, 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를!!! 변경하자.
  • 그러면 Transaction commit 시점에 변경감지 실행됨

 

7. GitHub : 210806 Merge, Dirty checking


 

GitHub - bsh6463/SpringBootJPA1

Contribute to bsh6463/SpringBootJPA1 development by creating an account on GitHub.

github.com

 

'인프런 > [인프런] Springboot와 JPA활용 1' 카테고리의 다른 글

주문 목록 검색 및 취소  (0) 2021.08.06
상품 주문  (0) 2021.08.06
상품 등록, 상품 목록, 상품 수정  (0) 2021.08.06
회원 목록 조회  (0) 2021.08.06
회원 등록  (0) 2021.08.06
Comments