[spring] 웹 계층개발 -3

변경 감지와 병합(merge)


JPA에서 변경 감지와 병합차이가 중요하다.

준영속 엔티티 = JPA 영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말함

직전의 상품수정할 때 처럼 한번 db에 들어갔다와서 식별자가 db에 있으면 준영속 객체라 함.
JPA식별 가능한 id가짐

Book 객체는 이미 DB에 한번 저장되어서 식별자가 존재한다. 이렇게 임의로 만들어낸 엔티티도 기존 식별자를 가지고 있으면 준영속 엔티티로 볼 수 있다.

준용속은 JPA가 관리를 안한다. -> new로 만들었음

준영속 엔티티 데이터 변경하는 방법 , 준영속 엔티티를 수정하는방법

1. 변경 감지 기능 사용
더티체킹(Dirty Checking)
값을 파라미터로 넣어서 세팅하면 @Transactional이 커밋되면서 플러쉬 날린다.
영속성 컨텍스트 변경된거 찾고 바뀐것에 대해 update 쿼리 날림

@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한
다.
findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다.
}

2. 병합 사용
(Merge)
병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능.

@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(item);
}

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

병합은 조심해야함
더티체킹은 원하는 속성만 선택해서 변경할 수 있지만, 병합은 모든 속성이 변경됨. (모든 필드 교체, 세팅 안하면 db에 null이 들어간다.)

merge 로 깔끔하게 하는 법은 없으므로 가급적 쓰지 말고

엔티티를 변경할 때는 항상 변경 감지로 쓰는 것이 실무에서 좋다.

컨트롤러에서 어설프게 엔티티를 생성X
트랜잭션이 있는 서비스 계층에 식별자( id )와 변경할 데이터를 명확하게 전달.(파라미터 or dto)
트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경하기
트랜잭션 커밋 시점에 변경 감지가 실행된다.

상품 주문


OrderController 클래스 생성

package jpabook.jpashop.controller;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.service.ItemService;
import jpabook.jpashop.service.MemberService;
import jpabook.jpashop.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

import java.util.List;

@Controller
@RequiredArgsConstructor
public class OrderController {
    private final OrderService orderService;
    private final MemberService memberService;
    private final ItemService itemService;

    @GetMapping("/order")
    public String createForm(Model model){
        List<Member> members = memberService.findMembers();
        List<Item> items = itemService.findItems();

        model.addAttribute("members", members);
        model.addAttribute("items", items);
        return "order/orderForm";
    }
}

상품 주문 폼 order/orderForm 생성하고 나면

잘 나옴

<option th:each="member : ${members}"
                th:value="${member.id}"
                th:text="${member.name}" />

each로 가지고 있는 member 목록 다 돌리게 구성되어있다.

PostMapping 도 해준다
@RequestParam은 form submit으로 value넘어오면 매칭되어서 변수에 binding 된다.

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

주문 목록 검색, 취소


@GetMapping("/orders")
    public String orderList(@ModelAttribute("orderSearch") OrderSearch orderSearch, Model model) {
        List<Order> orders = orderService.findOrders(orderSearch);
        model.addAttribute("orders", orders);
        return "order/orderList";
    }
}


검색이 잘 된다.

주문 취소 코드는

 @PostMapping(value = "/orders/{orderId}/cancel")
    public String cancelOrder(@PathVariable("orderId") Long orderId) {
        orderService.cancelOrder(orderId);
        return "redirect:/orders";
    }

취소도 잘 된다.

드디어 끄으읕 ~~ 이제 JPA2 강의로 가자

이제까지 사용한 코드는
https://github.com/Vector1331/jpashop
github에서 확인가능하다 .

좋은 웹페이지 즐겨찾기