# 도서 주문하기
다대다 관계를 학습하고 이를 통해 필요 도서를 장바구니에 담고 최종 주문에 이르는 프로세스를 구현한다.
## 목차
1. 다대다 관계란?
2. 실습하기: 도서 주문
+ View
- 장바구니에 담기 버튼
- 장바구니 페이지
- 주문완료 페이지
+ DB
- 모델링: carts -< items >- books
- 테이블 생성: carts, items
+ Model
- VO: Cart
- Mapper: CartMapper
+ Controller
- 도서 장바구니에 담기()
- 장바구니 주문하기()
- 주문내역 보기()
## Many-to-Many 관계란?
Many-to-Many(다대다) 관계란 데이터베이스의 개념으로, 다수의 데이터가 특정 관계에 의해 다수의 데이터와 연결되는 것을 나타낸다.
![Imgur](https://i.imgur.com/SNyVWaT.png)
위 그림을 통해 many-to-many 관계를 이해해보자. tom은 샌드위치와 파스타를 좋아한다. elena는 샐러드와 파스타를 좋아한다. 이렇게 다양한 사용자는 likes 관계를 통해 다양한 음식들에 연결되어있다.
역으로 음식의 입장에서 샌드위치는 tom의 기호음식이다. 그리고 샐러드는 elena의 기호음식이다. 파스타는 tom과 elena가 동시에 좋아하는 음식이다.
이러한 many-to-many 관계는 아래와 같은 Entity-Relationship-Diagram(ERD)로 표현할 수 있다.
![Imgur](https://i.imgur.com/dLUmJGc.png)
위에서 ERD로 표현한 데이터간 관계를 db-schema 다이어그램으로 표현하면 아래와 같다.
![Imgur](https://i.imgur.com/z8a7wCs.png)
## 실습 하기: 도서 주문
### View
#### 장바구니에 담기 버튼
![Imgur](https://i.imgur.com/yZH7Gdz.png)
`books/show.jsp`
```
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!-- jumbotron -->
<div class="jumbotron">
<h1 class="display-4">Show Book</h1>
<p class="lead">views/books/show.jsp</p>
<hr class="my-4">
<p>책 상세 페이지</p>
</div>
<div class="row">
<div class="col-md-4">
<img class="card-img-top" src="${ book.image }" alt="Card image cap">
</div>
<div class="col-md-8">
<h3>${ book.title }</h3>
<p>저자: ${ book.author }</p>
<p>가격: ${ book.price }원</p>
<hr class="my-4">
<form action="<c:url value='/carts/add' />" method="post">
<div class="form-group">
<label>수량</label>
<input name="amount" class="form-control" type="number" value="1" />
</div>
<input name="book_id" type="hidden" value="${ book.id }">
<button type="submit" class="btn btn-primary">장바구니에 담기</button>
</form>
</div>
</div>
```
#### 장바구니 페이지
![Imgur](https://i.imgur.com/UY05IVO.png)
`carts/index.jsp`
```
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!-- jumbotron -->
<div class="jumbotron">
<h1 class="display-4">Index Carts</h1>
<p class="lead">views/carts/index.jsp</p>
<hr class="my-4">
<p>장바구니 페이지</p>
</div>
<h2>장바구니<span class="badge badge-warning">쇼핑중</span></h2>
<hr>
<table class="table">
<thead class="thead-light">
<tr>
<th>#</th>
<th>도서명</th>
<th>가격</th>
<th>수량</th>
<th>합계</th>
</tr>
</thead>
<tbody>
<c:forEach var="item" items="${ items }" varStatus="status">
<tr>
<th>${ status.count }</th>
<td>${ item.title }</td>
<td>${ item.price }</td>
<td>${ item.amount }</td>
<td>${ item.price * item.amount }</td>
</tr>
</c:forEach>
</tbody>
<tfoot>
<tr>
<td colspan="4"></td>
<td>${ cart.totalPrice }</td>
</tr>
</tfoot>
</table>
<form action="<c:url value='/orders' />" method="post">
<input name="id" type="hidden" value="${ cart.id }" type="hidden" />
<button type="submit" class="btn btn-lg btn-block btn-primary">주문하기</button>
</form>
```
#### 주문완료 페이지
![Imgur](https://i.imgur.com/sJtqUwB.png)
`orders/index.jsp`
```
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!-- jumbotron -->
<div class="jumbotron">
<h1 class="display-4">Orders Index</h1>
<p class="lead">views/orders/index.jsp</p>
<hr class="my-4">
<p>주문 내역을 보여줌</p>
</div>
<h2>주문 내역</h2>
<hr>
<c:forEach var="order" items="${ orders }">
<h4><span class="badge badge-warning">출고중</span></h4>
<table class="table">
<thead class="thead-light">
<tr>
<th>#</th>
<th>도서명</th>
<th>가격</th>
<th>수량</th>
<th>합계</th>
</tr>
</thead>
<tbody>
<c:forEach var="item" items="${ order.items }" varStatus="status">
<tr>
<th>${ status.count }</th>
<td>${ item.title }</td>
<td>${ item.price }</td>
<td>${ item.amount }</td>
<td>${ item.price * item.amount }</td>
</tr>
</c:forEach>
</tbody>
<tfoot>
<tr>
<td colspan="4"></td>
<td>${ order.totalPrice }</td>
</tr>
</tfoot>
</table>
</c:forEach>
```
### DB
#### 데이터 모델링
![Imgur](https://i.imgur.com/Fr2SFVM.png)
#### 테이블 생성
```
create table carts (
id serial primary key,
status integer default 0,
user_id integer references users not null
);
create table items (
cart_id integer references carts not null,
book_id integer references books not null,
amount integer not null
);
```
### Model
#### VO
`CartVO.java`
```
public class CartVO {
public static final int SHOPPING = 0;
public static final int ORDERD = 1;
public static final int SHIPPING = 2;
public static final int DELYVERED = 3;
private int id;
private int status;
private int user_id;
private List<ItemVO> items;
private int totalPrice;
public CartVO() {
}
public int getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(int totalPrice) {
this.totalPrice = totalPrice;
}
public CartVO(int status, int user_id) {
this.status = status;
this.user_id = user_id;
}
public List<ItemVO> getItems() {
return items;
}
public void setItems(List<ItemVO> items) {
this.items = items;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
@Override
public String toString() {
return "CartVO [id=" + id + ", status=" + status + ", user_id=" + user_id + "]";
}
}
```
`ItemVO.java`
```
public class ItemVO {
private int cart_id;
private int book_id;
private int amount;
private BookVO book;
public BookVO getBook() {
return book;
}
public void setBook(BookVO book) {
this.book = book;
}
public String getTitle() {
return book.getTitle();
}
public int getPrice() {
return book.getPrice();
}
public int getCart_id() {
return cart_id;
}
public void setCart_id(int cart_id) {
this.cart_id = cart_id;
}
public int getBook_id() {
return book_id;
}
public void setBook_id(int book_id) {
this.book_id = book_id;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
@Override
public String toString() {
return "ItemVO [cart_id=" + cart_id + ", book_id=" + book_id + ", amount=" + amount + "]";
}
}
```
#### Mapper
`CartMapper.java`
```
public interface CartMapper {
@Insert("insert into carts (user_id, status) values (#{user_id}, #{status})")
@Options(useGeneratedKeys=true, keyProperty="id")
int create(CartVO cartVO);
@Insert("insert into items (cart_id, book_id, amount) values (#{cart_id}, #{book_id}, #{amount})")
void add(ItemVO item);
@Select("select * from items where cart_id = #{id}")
List<ItemVO> getItems(CartVO cart);
@Update("update carts set status = 1 where id = #{id}")
void order(CartVO cart);
}
```
`UserMapper.java`
```
public interface UserMapper {
@Select("SELECT * FROM users WHERE email = #{email}")
public UserVO selectUserByEmail(String email);
@Select("select exists(select 1 from carts where status = #{status})")
boolean hasCart(int status);
@Select("select * from carts where user_id = #{userId} and status = 0")
public CartVO getCart(int userId);
@Select("select * from carts where user_id = #{userId} and status > 0")
public List<CartVO> getOrders(int userId);
}
```
### Controller
`CartsController.java`
```
@Controller
public class CartsController {
@Autowired
private UserMapper userMapper;
@Autowired
private CartMapper cartMapper;
@Autowired
private BookMapper bookMapper;
@RequestMapping(value = "/carts", method = RequestMethod.GET)
public String index(Principal principal, Model model) {
// 현재 사용자
int currUserId = userMapper.selectUserByEmail(principal.getName()).getId();
// 카트에
CartVO cart = userMapper.getCart(currUserId);
if (cart != null) {
// 담긴 아이템
List<ItemVO> items = cartMapper.getItems(cart);
for (ItemVO i : items) {
i.setBook(bookMapper.getBook(i.getBook_id())); // 아이템이 가리키는 책
}
// 합계
int sum = 0;
for (ItemVO i : items) {
sum += i.getPrice() * i.getAmount();
}
cart.setTotalPrice(sum);
model.addAttribute("cart", cart);
model.addAttribute("items", items);
}
return "carts/index";
}
@RequestMapping(value = "/carts/add", method = RequestMethod.POST)
public String add(@ModelAttribute ItemVO item, Principal principal) {
// 현재 사용자
UserVO currentUser = userMapper.selectUserByEmail(principal.getName());
// 쇼핑카트(현재 사용자 소유) 번호를 아이템에 저장
if (!userMapper.hasCart(CartVO.SHOPPING)) {
int cartId = cartMapper.create(new CartVO(CartVO.SHOPPING, currentUser.getId()));
item.setCart_id(cartId);
} else {
item.setCart_id(userMapper.getCart(currentUser.getId()).getId());
}
// 쇼핑카트에 아이템을 담음
cartMapper.add(item);
return "redirect:/carts";
}
}
```
`OrdersController.java`
```
@Controller
public class OrdersController {
@Autowired
private UserMapper userMapper;
@Autowired
private CartMapper cartMapper;
@Autowired
private BookMapper bookMapper;
@RequestMapping(value = "/orders", method = RequestMethod.POST)
public String create(@ModelAttribute CartVO cart) {
cartMapper.order(cart);
return "redirect:/orders";
}
@RequestMapping(value = "/orders", method = RequestMethod.GET)
public String index(Principal principal, Model model) {
// 현재 사용자
int currUserId = userMapper.selectUserByEmail(principal.getName()).getId();
// 주문 목록
List<CartVO> orders = userMapper.getOrders(currUserId);
// 주문별 아이템
for (CartVO order : orders) {
order.setItems(cartMapper.getItems(order));
// 아이템-책
int sum = 0;
for (ItemVO i : order.getItems()) {
i.setBook(bookMapper.getBook(i.getBook_id()));
sum += i.getPrice() * i.getAmount();
}
order.setTotalPrice(sum);
}
model.addAttribute("orders", orders);
return "orders/index";
}
}
```