jpa-spring boot 실습 - 5

김강현·2023년 4월 9일
0

SPRING-JPA-실습

목록 보기
5/5
post-thumbnail
  • Logger 를 사용할때는 import org.slf4j.Logger; 를 사용할 것
  • @Slf4j lombok 에서 해당 어노테이션 지원! 바로 코드에서 log 로 사용 가능!
  • Spring boot 에서 지원해주는 것 : re-run 을 하지 않고, ctrl + shift + F9 으로 즉시 컴파일 가능!

웹 계층 세팅

  • boot strap 주입
  • 각종 html 파일 세팅

ctrl + E 최근에 썼던 명령어, 리소스 뜸!
ctrl + alt + shift + T 리팩토링 관련 추천!
ctrl + shift + U 해당 문자 upper case 로 변환

변경 감지와 병합 (merge)

변경 감지 (dirty checking)

영속성 컨텍스트에 올라와 있는 엔티티 에 대해서는, 어떤 변경들이 일어나더라도,
EntityManager 가 flush 되는 시점에 감지를 하여 sql 문이 날아간다.

but, 준영속 엔티티는 어떻게 해야하나?

  • 영속성 컨텍스트가 더는 관리하지 않는 엔티티
  • 하지만, jpa DB를 한번 거쳐서, 식별자는 가지고 있는 경우!
  • 임의로 만든 엔티티 여도, 기존 식별자를 가지고 있으면, 준영속 엔티티 로 볼 수 있다.

    직접적으로 JPA 가 관리를 하지 않기에, 수정 이후 영속성 컨텍스트에 올려주어야함.
    다른 방법은 없나? HOW?

      1. 변경 감지 기능
      1. 병합 (merge) 사용

준영속 경우 업데이트 기존

    @PostMapping("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";
    }

1. 변경 감지 활용

  • Service 계층에서 호출하면서, 영속성 컨텍스트에 올리고,
  • 마음껏 수정 (flush 때 인식하여, db sql 문 날림)
    @PostMapping("items/{itemId}/edit")
    public String updateItem( @PathVariable String itemId, @ModelAttribute("form") BookForm form){
        itemService.updateItem(itemId, form); // Service 계층에 메소드 추가
        return "redirect:/items";
    }

< ItemService.java >

    @Transactional
    public void updateItem(Long itemId, BookForm form){
        Book book = (Book) itemRepository.findOne(itemId);
        book.setName(form.getName());
        book.setPrice(form.getPrice());
        book.setStockQuantity(form.getStockQuantity());
        book.setAuthor(form.getAuthor());
        book.setIsbn(form.getIsbn());
    }

이 부분도, 하나하나 setter 로 바꾸는 것 보다는, book 단의 Set Method 를 따로 만들어서, 하는 것을 추천!
우리 협업 좀 하자!! 몇달 뒤 너를 위해서라도! 동료 파트너를 위해서라도!

    @Transactional
    public void updateItem(Long itemId, BookForm form){
        Book book = (Book) itemRepository.findOne(itemId);
        book.changeWholeField(form.getName(),
                              form.getPrice(),
                              form.getStockQuantity(),
                              form.getAuthor(),
                              form.getIsbn());
    }

2. 병합 (merge) 활용

< ItemRepository.java >

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

위의 변경감지랑 똑같은 flow 로 작동함!!
다만!!

불러온 값에서, merge 한 엔티티에 있는 값들로 전부 바꿔치기함!!
즉 영속성 컨텍스트 속에서는 내가 원하는 수정 상태로 가지고 있고, flush 하면 업데이트 sql 이 실행됨.

주의 사항!!

  • item 을 merge 했다고 해서, item 자체가 영속성 컨텍스트 위에 있지는 x
    -- 영속성 엔티티를 활용하고 싶으면, em.merge() 의 반환값을 사용해야함!!
  • 변경감지를 사용하면, 원하는 속성만 변경 가능하지만, 병합은 모든 속성이 변경됨 (null 값들도)
    -- 수정을 원치 않던 값에 null 이 들어가서 기존 DB의 값을 덮어버리는 대참사 가능성...
  • 가급적 (1) 변경감지 를 사용하는 것이 맞음. 보통 update는 몇가지만 일어나니!!

서비스 계층에서의 엔티티 관리

Controller 계층에서의 엔티티

  • 바로 위 변경감지에서 Book Entity를 넘기지 않고, idBookForm 을 service 계층에 파라미터로 전달
  • @Transactional 을 가지고 있는 서비스 계층에서, 엔티티의 시작과 끝을 관리하는 것이 좋음
  • 파라미터 값으로는 엔티티와 최대한 연관이 없도록 분리 시켜주는 것이 유지 보수 등 코드에 유리!

< OrderController.java >

    @PostMapping("/order")
    public String order(@RequestParam("memberId") Long memberId,
                        @RequestParam("itemId") Long itemId,
                        @RequestParam("count") int count){
        orderService.order(memberId, itemId, count);
        return "redirect:/orders";
    }

이런 식으로 service 계층에 엔티티를 직접 넘기지 않는 방법이 좋음!!

profile
this too shall pass

0개의 댓글