티스토리 뷰

반응형

| Web Application (Spring + JPA) 2.


--

1. 프로젝트 환경설정

2. 도메일 모델과 테이블 설게

3. 애플리케이션 기능 구현



|| 프로젝트 환경 설정


--

prior post : [Spring + JPA] Make Web Application (1)



|| 도메인 모델과 테이블 설계


--

prior post : [Spring + JPA] Make Web Application (1)




|| 애플리케이션 기능 구현


--


||| 개발 방법


--

* Controller : MVC Controller가 모여 있는 곳

  Controller는 Service 계층을 호출하고 결과를 뷰(JSP)에 전달

* Service : Service 계층에는 비즈니스 로직이 있고 트랜잭션을 시작

 Service 계층은 데이터 접근 계층인 repository 호출

* Repository : JPA를 직접 사용하는 계층

엔티티 매니저를 사용해서 엔티티를 저장하고 조회

* Domain : 엔티티가 모여 있는 계층, 모든 계층에서 사용


> 개발 순서

0. Domain

1. Service (Business Logic) & Repository

2. Controller

3. JSP



||| 회원 기능


--

회원 등록

회원 목록 조회


> Member Repository

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
32
33
34
35
36
import jpabook.jpashop.domain.Member;
import org.springframework.stereotype.Repository;
 
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
 
@Repository
public class MemberRepository {
 
    @PersistenceContext
    EntityManager em;
 
    // 회원 엔티티 저장(영속화)
    public void save(Member member) {
        em.persist(member);
    }
 
    // 회원 식별자로 회원 엔티티 조회
    public Member findOne(Long id) {
        return em.find(Member.class, id);
    }
 
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
 
    // JPQL을 사용하여 이름으로 회원 엔티티 조회
    public List<Member> findByName(String name) {
        return em.createQuery("select m from Member m where m.name = :name", Member.class)
                .setParameter("name", name)
                .getResultList();
    }
}
 
cs

@Repository

- <context:component-scan>에 의해 Spring Bean으로 자동 등록

- JPA 전용 예외 발생 시 스피링이 추상화한 예외로 변환

@PersistenceContext

- 컨테이너가 관리하는 엔티티 매니저 주입

- spring에서는 컨테이너가 엔티티 매니저를 관리 및 제공

  (엔티티 매니저 팩토리에서 엔티티 매니저를 직접 생성하지 않고 컨테이너가 제공하는 엔티티 매니저 사용)

@PersistenceUnit

- 엔티티 매니저 팩토리 주입 (직접 사용할 경우)


> Member Service

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
32
33
34
35
36
37
38
39
40
41
42
43
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.List;
 
@Service
@Transactional
public class MemberService {
 
    @Autowired
    MemberRepository memberRepository;
 
    /**
     * 회원 가입
     */
    public Long join(Member member) {
 
        validateDuplicateMember(member); //중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }
 
    private void validateDuplicateMember(Member member) {
        List<Member> findMembers = memberRepository.findByName(member.getName());
        if (!findMembers.isEmpty()) {
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        }
    }
 
    /**
     * 전체 회원 조회
     */
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }
 
    public Member findOne(Long memberId) {
        return memberRepository.findOne(memberId);
    }
}
cs

@Service

- <context:component-scan>에 의해 Spring Bean으로 자동 등록

@Transactional

- 트랜잭션을 적용

- 외부에서 이 클래스의 메소드를 호출할 때 트랜잭션을 시작하고, 메소드를 종료할 때 트랜잭션을 커밋

- 예외 발생 시 트랜잭션 롤백

+ 체크 예외가 발생해도 롤백 설정 @Transactional(rollbackFor = Exception.class)

@Autowired

- spring container가 적절한 spring bean을 주입


> Member Service Test

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package jpabook.jpashop.service;
 
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.transaction.annotation.Transactional;
 
import static org.junit.Assert.*;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:appConfig.xml")
@Transactional
public class MemberServiceTest {
 
    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;
 
    @Test
    public void 회원가입() throws Exception {
 
        // Given (테스트할 상황 설정)
        Member member = new Member();
        member.setName("kim");
 
        // When (테스트 대상 실행)
        Long saveId = memberService.join(member);
 
        // Then (결과 검증)
        assertEquals(member, memberRepository.findOne(saveId));
    }
 
    @Test(expected = IllegalStateException.class)
    public void 중복_회원_예외() throws Exception {
 
        //Given
        Member member1 = new Member();
        member1.setName("kim");
 
        Member member2 = new Member();
        member2.setName("kim");
 
        //When
        memberService.join(member1);
        memberService.join(member2); // 회원가입 시 같은 이름이 있을 경우 예외가 발생해야 한다.
 
        //Then
        fail("예외가 발생해야 한다.");
    }
 
 
}
cs


@RunWith(SpringJUnit4ClassRunner.class)

- Test Case를 spring framework와 통합

- 테스트가 스프링 컨테이너에서 실행 (@Autowired 같은 기능 사용 가능)

@ContextConfiguration(locations = "classpath:appConfig.xml")

- 테스트 케이스를 실행할 때 사용할 스프링 설정 정보 지정

@Transactional

- 테스트에서 사용 시 각각의 테스트를 실행할 때마다 트랜잭션을 시작하고 테스트가 끝나면 트랜잭션을 강제로 롤백

- 반복해서 테스트를 진행


@Test(expected = IllegalStateException.class)

- expected 속성에 예외 클래스를 지정하면 테스트 결과로 지정한 예외가 발생해야 테스트 성공



||| 상품 기능


--

상품 등록

상품 목록 조회

상품 수정


> Item Repository

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
32
33
import jpabook.jpashop.domain.item.Item;
import org.springframework.stereotype.Repository;
 
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
 
@Repository
public class ItemRepository {
 
    @PersistenceContext
    EntityManager em;
 
    // 상품 저장과 수정 처리
    public void save(Item item) {
        // 식별자 값이 없을 경우 영속화
        if (item.getId() == null) {
            em.persist(item);
        } 
        // 식벽자 값이 있을 경우 수정(병합)
        else {
            em.merge(item);
        }
    }
 
    public Item findOne(Long id) {
        return em.find(Item.class, id);
    }
 
    public List<Item> findAll() {
        return em.createQuery("select i from Item i",Item.class).getResultList();
    }
}
cs


> Item Service

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
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.repository.ItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.List;
 
@Service
@Transactional
public class ItemService {
 
    @Autowired
    ItemRepository itemRepository;
 
    public void saveItem(Item item) {
        itemRepository.save(item);
    }
 
    public List<Item> findItems() {
        return itemRepository.findAll();
    }
 
    public Item findOne(Long itemId) {
        return itemRepository.findOne(itemId);
    }
}
cs



||| 주문 기능


--

상품 주문

주문 내역 조회

주문 취소


> Order Repository

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderSearch;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
 
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
 
@Repository
public class OrderRepository {
 
    @PersistenceContext
    EntityManager em;
 
    public void save(Order order) {
        em.persist(order);
    }
 
    public Order findOne(Long id) {
        return em.find(Order.class, id);
    }
 
    public List<Order> findAll(OrderSearch orderSearch) {
 
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Order> cq = cb.createQuery(Order.class);
        Root<Order> o = cq.from(Order.class);
 
        List<Predicate> criteria = new ArrayList<Predicate>();
 
        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null) {
            Predicate status = cb.equal(o.get("status"), orderSearch.getOrderStatus());
            criteria.add(status);
        }
        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            Join<Order, Member> m = o.join("member", JoinType.INNER); //회원과 조인
            Predicate name = cb.like(m.<String>get("name"), "%" + orderSearch.getMemberName() + "%");
            criteria.add(name);
        }
 
        cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
        TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000); //최대 검색 1000 건으로 제한
        return query.getResultList();
    }
}
cs


> Order Service

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import jpabook.jpashop.domain.Delivery;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderItem;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.domain.OrderSearch;
import jpabook.jpashop.repository.MemberRepository;
import jpabook.jpashop.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.List;
 
@Service
@Transactional
public class OrderService {
 
    @Autowired MemberRepository memberRepository;
    @Autowired OrderRepository orderRepository;
    @Autowired ItemService itemService;
 
    /** 주문 */
    public Long order(Long memberId, Long itemId, int count) {
 
        //엔티티 조회
        Member member = memberRepository.findOne(memberId);
        Item item = itemService.findOne(itemId);
 
        //배송정보 생성
        Delivery delivery = new Delivery(member.getAddress());
        //주문상품 생성
        OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
        //주문 생성
        Order order = Order.createOrder(member, delivery, orderItem);
 
        //주문 저장
        orderRepository.save(order);
        return order.getId();
    }
 
 
    /** 주문 취소 */
    public void cancelOrder(Long orderId) {
 
        //주문 엔티티 조회
        Order order = orderRepository.findOne(orderId);
 
        //주문 취소
        order.cancel();
    }
 
    /** 주문 검색 */
    public List<Order> findOrders(OrderSearch orderSearch) {
        return orderRepository.findAll(orderSearch);
    }
 
}
cs


line 24~40) 주문하는 회원 식별자, 상품 식별자, 주문 수량 정보로 주문 엔티티 생성 후 저장

line 44~51) 주문 식별자로 주문 엔티티를 조회한 후 주문 엔티티에 주문 최소 요청

line 54~56) 검색 조건을 가진 OrderSearch 객체로 주문 엔티티 검색


> OrderSearch (검색 조건을 가진 객체)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class OrderSearch {
 
    private String memberName;          //회원 이름
    private OrderStatus orderStatus;    //주문 상태(ORDER, CANCEL)
 
    //Getter, Setter
    public String getMemberName() {
        return memberName;
    }
 
    public void setMemberName(String memberName) {
        this.memberName = memberName;
    }
 
    public OrderStatus getOrderStatus() {
        return orderStatus;
    }
 
    public void setOrderStatus(OrderStatus orderStatus) {
        this.orderStatus = orderStatus;
    }
}
cs


> Order Service Test

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderStatus;
import jpabook.jpashop.domain.item.Book;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.exception.NotEnoughStockException;
import jpabook.jpashop.repository.OrderRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
 
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
 
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:appConfig.xml")
@Transactional
//@TransactionConfiguration(defaultRollback = false)
public class OrderServiceTest {
 
    @PersistenceContext
    EntityManager em;
 
    @Autowired OrderService orderService;
    @Autowired OrderRepository orderRepository;
 
    @Test
    public void 상품주문() throws Exception {
 
        //Given
        Member member = createMember();
        Item item = createBook("시골 JPA"1000010); //이름, 가격, 재고
        int orderCount = 2;
 
        //When
        Long orderId = orderService.order(member.getId(), item.getId(), orderCount);
 
        //Then
        Order getOrder = orderRepository.findOne(orderId);
 
        assertEquals("상품 주문시 상태는 주문(ORDER)이다.", OrderStatus.ORDER, getOrder.getStatus());
        assertEquals("주문한 상품 종류 수가 정확해야 한다."1, getOrder.getOrderItems().size());
        assertEquals("주문 가격은 가격 * 수량이다."10000 * 2, getOrder.getTotalPrice());
        assertEquals("주문 수량만큼 재고가 줄어야 한다."8, item.getStockQuantity());
    }
 
    @Test(expected = NotEnoughStockException.class)
    public void 상품주문_재고수량초과() throws Exception {
 
        //Given
        Member member = createMember();
        Item item = createBook("시골 JPA"1000010); //이름, 가격, 재고
 
        int orderCount = 11//재고 보다 많은 수량
 
        //When
        orderService.order(member.getId(), item.getId(), orderCount);
 
        //Then
        fail("재고 수량 부족 예외가 발생해야 한다.");
    }
 
 
    @Test
    public void 주문취소() {
 
        //Given
        Member member = createMember();
        Item item = createBook("시골 JPA"1000010); //이름, 가격, 재고
        int orderCount = 2;
 
        Long orderId = orderService.order(member.getId(), item.getId(), orderCount);
 
        //When
        orderService.cancelOrder(orderId);
 
        //Then
        Order getOrder = orderRepository.findOne(orderId);
 
        assertEquals("주문 취소시 상태는 CANCEL 이다.", OrderStatus.CANCEL, getOrder.getStatus());
        assertEquals("주문이 취소된 상품은 그만큼 재고가 증가해야 한다."10, item.getStockQuantity());
    }
 
    private Member createMember() {
        Member member = new Member();
        member.setName("회원1");
        member.setAddress(new Address("서울""강가""123-123"));
        em.persist(member);
        return member;
    }
 
    private Book createBook(String name, int price, int stockQuantity) {
        Book book = new Book();
        book.setName(name);
        book.setStockQuantity(stockQuantity);
        book.setPrice(price);
        em.persist(book);
        return book;
    }
}
 
cs


line 35~52) 상품 주문 검증

 주문 상태, 주문 상품, 주문 가격, 주문 후 재고 수량 검증

line 55~68) 재고 수량 초과 검증

 재고 수량 초과 시 NotEnoughStockException 예외 발생

line 72~89) 주문 취소 검증

 주문 취소 상태, 주문 취소 후 재고 수량 검증



||| 웹 계층 구현


--

> Item Controller

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import jpabook.jpashop.domain.item.Book;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
import java.util.List;
 
@Controller
public class ItemController {
 
    @Autowired ItemService itemService;
 
    @RequestMapping(value = "/items/new", method = RequestMethod.GET)
    public String createForm() {
        return "items/createItemForm";
    }
 
    @RequestMapping(value = "/items/new", method = RequestMethod.POST)
    public String create(Book item) {
 
        itemService.saveItem(item);
        return "redirect:/items";
    }
 
    /**
     * 상품 수정 폼
     */
    @RequestMapping(value = "/items/{itemId}/edit", method = RequestMethod.GET)
    public String updateItemForm(@PathVariable("itemId") Long itemId, Model model) {
 
        Item item = itemService.findOne(itemId);
        model.addAttribute("item", item);
        return "items/updateItemForm";
    }
 
    /**
     * 상품 수정
     */
    @RequestMapping(value = "/items/{itemId}/edit", method = RequestMethod.POST)
    public String updateItem(@ModelAttribute("item") Book item) {
 
        itemService.saveItem(item);
        return "redirect:/items";
    }
 
    /**
     * 상품 목록
     */
    @RequestMapping(value = "/items", method = RequestMethod.GET)
    public String list(Model model) {
 
        List<Item> items = itemService.findItems();
        model.addAttribute("items", items);
        return "items/itemList";
    }
 
}
cs


ㅇ 상품 등록

line 19~22) 상품 등록을 누르면 "/items/new" URL을 HTTP GET 방식으로 요청하고

"items/createItemForm.jsp" 로 전달

line 24~29) createItemForm.jps 에서 정보를 입력

<form role="form" action="/items/new" method="post"> 를 통해

파라미터로 전달한 Item에는 화면에서 입력한 데이터가 모두 바인딩

(HttpServletRequest 의 파라미터와 객체의 프로퍼티 이름을 비교해서 같으면 스프링 프레임워크가 값을 바인딩)

Item 저장(saveItem) 요청 후 "/items" URL로 Redirect


ㅇ 상품 목록

line 55~61) 상품 목록을 선택하면 "/items" URL 로 이동

전체 Item을 조회하고 Model 객체에 담은 후 "item/itemList.jsp" 로 전달


ㅇ 상품 수정

line 34~40) 수정 버튼을 선택하면 "/items/{itemId}/edit" URL을 HTTP GET 방식으로 요청

Model 객체에 수정할 item 정보를 담고 "items/updateItemForm.jsp" 로 전달

line 45~50) 상품 수정 후 Submit 버튼을 선택하면 "/items/{itemId}/edit" URL을 HTTP POST 방식으로 요청

컨트롤러의 파라미터로 넘어온 item 엔티티 인스턴스는 준영속 상태이므로 변경 감지 기능이 동작하지 않음


> Order Controller

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.domain.OrderSearch;
import jpabook.jpashop.service.ItemService;
import jpabook.jpashop.service.MemberService;
import jpabook.jpashop.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
 
import java.util.List;
 
@Controller
public class OrderController {
 
    @Autowired OrderService orderService;
    @Autowired MemberService memberService;
    @Autowired ItemService itemService;
 
    @RequestMapping(value = "/order", method = RequestMethod.GET)
    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";
    }
 
    @RequestMapping(value = "/order", method = RequestMethod.POST)
    public String order(@RequestParam("memberId") Long memberId, @RequestParam("itemId") Long itemId, @RequestParam("count"int count) {
 
        orderService.order(memberId, itemId, count);
        return "redirect:/orders";
    }
 
    @RequestMapping(value = "/orders", method = RequestMethod.GET)
    public String orderList(@ModelAttribute("orderSearch") OrderSearch orderSearch, Model model) {
 
        List<Order> orders = orderService.findOrders(orderSearch);
        model.addAttribute("orders", orders);
 
        return "order/orderList";
    }
 
    @RequestMapping(value = "/orders/{orderId}/cancel")
    public String processCancelBuy(@PathVariable("orderId") Long orderId) {
 
        orderService.cancelOrder(orderId);
 
        return "redirect:/orders";
    }
}
cs


ㅇ 상품 주문

line 22~32) 상품 주문 선택 시 "/order" URL을 HTTP GET 방식으로 요청하고

 고객 정보와 상품 정보를 모델 객체에 담아서 "orderForm.jsp"로 전달

line 34~39) 상품 주문 정보를 입력한 후 Submit 버튼을 선택하면

 <form role="form" action="/order" method="post"> 를 통해 주문 엔티티 생성

 주문이 끝나면 "/orders" URL로 Redirect


ㅇ 주문 내역

line 41~48) 주문 내역을 선택하면

<form class="navbar-form navbar-left" role="search"> 를 통해

orderSearch 객체를 통해 주문 내역을 담고 "order/orderList.jsp"로 전달


ㅇ 주문 취소

line 50~56) 주문 취소 버튼을 선택할 시 해당 주문 ID에 해당하는 주문을 취소한 후

 "/orders" URL로 Redirect



||| 변경 감지와 병합


--

ㅇ 파라미터로 넘어온 준영속 상태의 엔티티를 수정하는 방법

> 변경 감지 기능 사용

- 영속성 컨텍스트에서 엔티티를 다시 조회한 후 데이터를 수정하는 방법

- 준영속 엔티티의 식별자로 엔티티를 다시 조회하면 영속 상태의 엔티티를 얻을 수 있음

  이렇게 영속 상태인 엔티티의 값을 파라미터로 넘어온 준영속 상태의 엔티티 값으로 변경

- 이후 트랜잭션이 커밋될 때 변경 감지 기능이 동작해서 데이터베이스에 수정사항이 반영

> 병합 사용

- 준영속 엔티티 식별자 값으로 영속 엔티티를 조회

- 영속 엔티티의 값을 준영속 엔티티의 값으로 채워 넣기


* 변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만,

  병합을 사용하면 모든 속성이 변경




|| Spring Data JPA 적용


--

[Spring Data JPA] Make Web Application (3)




출처 : 자바 ORM 표준 JPA 프로그래밍

https://github.com/holyeye/jpabook/tree/master/ch11-jpa-shop

반응형

'Books' 카테고리의 다른 글

[Spring + JPA] Spring Data JPA 란? (2)  (0) 2020.12.28
[Spring + JPA] Spring Data JPA 란? (1)  (2) 2020.12.24
[Spring + JPA] Make Web Application (1)  (0) 2020.12.23
[JPA] 벌크 연산이란?  (3) 2020.12.23
[JPA] 네이티브 SQL 정리  (0) 2020.12.23
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday