1. Product 관리 - 등록, 수정, 삭제
- ProductDto (상품 리스트 반환할 때 쓰인다.)
- 엔티티인 Product를 직접 사용해서 어떤 코드를 작성하면 참조형 데이터의 특성상 데이터베이스의 내용이 바로 변경될 우려가 있다.
- 그래서 Dto라는 클래스를 이용한다.
<hide/>
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductDto {
private Long id; // 상품 테이블의 PK
// 상품 리스트를 반환할 때 필요한 정보
private String productName;
private Integer price; // 상품 가격
private Boolean soldOutYn; // 품절 여부
private String productImg; // 상품 이미지 사진?
@Enumerated(EnumType.STRING)
private Category category; // 딱 한개
// @ManyToOne(fetch = LAZY)
// @JoinColumn(name = "CART_ID") // 외래 키 매핑 - CART 테이블의 PK id와 JOIN
// private Cart cart;
// 재고 관련 사항
private Integer productQuantity; // 재고 수량
private LocalDateTime stockedDt; // 입고 예정일 => 한 번에 1000개씩 들어온다 가정?
// 추가 컬럼
private Integer cumulativeSales; // 상품별 누적 판매 개수
// 페이징 위한 컬럼
long totalCount;
long seq;
// of() 오버로딩
public static List<ProductDto> of(List<Product> productList) {
if (productList != null) {
List<ProductDto> productDtoList = new ArrayList<>();
for (Product product : productList) {
productDtoList.add(of(product));
}
return productDtoList;
}
return null;
}
static ProductDto of(Product parameter) {
return ProductDto.builder()
.category(parameter.getCategory())
.cumulativeSales(parameter.getCumulativeSales())
.price(parameter.getPrice())
.productImg(parameter.getProductImg())
.productName(parameter.getProductName())
.productQuantity(parameter.getProductQuantity())
.soldOutYn(parameter.getSoldOutYn())
.stockedDt(parameter.getStockedDt())
.build();
}
}
- ProductInput (상품 수정)
<hide/>
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ProductInput {
// 상품 수정하기 위한 매개 변수, 입력값
private Long id; // 상품 테이블의 PK
private String productName;
private Integer price;
// private Boolean soldOutYn;
private String productImg;
@Enumerated(EnumType.STRING)
private Category category;
// 카트를 입력할 일은 없음
// @ManyToOne(fetch = LAZY)
// @JoinColumn(name = "CART_ID") // 외래 키 매핑 - CART 테이블의 PK id와 JOIN
// private Cart cart;
// 재고 관련 사항
private Integer productQuantity; // 재고 수량
private LocalDateTime stockedDt; // 입고 예정일 => 한 번에 1000개씩 들어온다 가정?
}
- ProductParam - id만 가지고 상품을 찾을 때 쓸 수 있다.
<hide/>
@Data
public class ProductParam extends CommonParam {
long id; // 상품 id
}
- AdminProductController
- 상품 update는 바뀔 수도 있음
<hide/>
package com.example.mall.admin.controller;
import com.example.mall.admin.model.ProductDto;
import com.example.mall.admin.model.ProductInput;
import com.example.mall.admin.model.ProductParam;
import com.example.mall.admin.service.ProductService;
import com.example.mall.controller.BaseController;
import com.example.mall.member.service.MemberService;
import java.util.List;
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.PostMapping;
@Controller
@RequiredArgsConstructor
public class AdminProductController extends BaseController {
private final ProductService productService;
private final MemberService memberService;
@GetMapping("/admin/product/list.do")
public String list(Model model,
ProductParam param) {
param.init(); // 페이지 초기화
List<ProductDto> list = productService.list();
long totalCount = 0;
if (list != null && list.size() > 0) {
totalCount = productService.list().size();
}
String queryString = param.getQueryString();
String pagerHtml = getPaperHtml(totalCount,
param.getPageSize(),
param.getPageIndex(),
queryString);
model.addAttribute("list", list);
model.addAttribute("totalCount", totalCount);
model.addAttribute("pager", pagerHtml);
model.addAttribute("list", list);
return "admin/product/list";
}
@PostMapping("/admin/product/add.do")
public String add(Model model, ProductInput parameter) {
boolean result = productService.add(parameter);
return "redirect:/admin/product/list.do";
}
@PostMapping("/admin/product/delete.do")
public String del(Model model, ProductInput parameter) {
boolean result = productService.delete(parameter.getId());
return "redirect:/admin/product/list.do";
}
@PostMapping("/admin/product/update.do")
public String update(Model model, ProductInput parameter) {
boolean result = productService.update(parameter);
return "redirect:/admin/product/list.do";
}
}
- ProductService
<hide/>
public interface ProductService {
/**
* 상품 신규 추가
*/
boolean add(ProductInput product); // 인풋으로 바꿔야할 수도 있다.
/**
* 상품 수정
*/
boolean update(ProductInput parameter);
/**
* 상품 삭제
*/
boolean delete(long id); // 기본키
/**
* 상품 리스트
*/
List<ProductDto> list();
}
========================오류 - update() 가 작동하지 않는다. ========================
- 오류: update()를 실행해도 데이터베이스에 들어가지 않는다.
- 원인: product를 save()하지 않았다.
<hide/>
package com.example.mall.admin.service.impl;
import com.example.mall.admin.model.ProductDto;
import com.example.mall.admin.model.ProductInput;
import com.example.mall.admin.repository.ProductRepository;
import com.example.mall.admin.service.ProductService;
import com.example.mall.entity.Product;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ProductServiceImpl implements ProductService {
private final ProductRepository productRepository;
// 등록
@Override
public boolean add(ProductInput parameter) {
Product product = Product.builder().productName(parameter.getProductName())
.productImg(parameter.getProductImg()).productQuantity(parameter.getProductQuantity())
.price(parameter.getPrice()).category(parameter.getCategory())
.stockedDt(parameter.getStockedDt()).build();
productRepository.save(product);
return true;
}
/*
// 수정
@Override
public boolean update(ProductInput parameter) { // id로 찾아서 다른 값들을 바꾼다.
Optional<Product> OptionalProduct = productRepository.findById(parameter.getId());
if (OptionalProduct.isPresent()) {
Product product = OptionalProduct.get();
product.setProductImg(parameter.getProductImg());
product.setProductName(parameter.getProductName());
product.setProductQuantity(parameter.getProductQuantity());
product.setCategory(parameter.getCategory());
product.setPrice(parameter.getPrice());
product.setStockedDt(parameter.getStockedDt());
}
return true;
}
*/
// 삭제
@Override
public boolean delete(long id) {
Optional<Product> OptionalProduct = productRepository.findById(id);
productRepository.delete(OptionalProduct.get());
return true;
}
@Override
public List<ProductDto> list() {
return ProductDto.of(productRepository.findAll());
}
}
- list.html
<hide/>
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>관리자</title>
<style>
.list table {
width: 100%;
border-collapse: collapse;
}
.list table th, .list table td {
border: solid 1px #000;
}
.search-form {
position: relative;
padding: 5px 0 5px 0;
text-align: right;
}
.search-form .total-count {
position-position: absolute;
left: 0;
top: 0;
height: 20px;
float: left;
}
.pager {
margin-top: 10px;
text-align: center;
}
.pager a.on {
font-weight: bold;
color: red;
}
</style>
</head>
<body>
<h1>관리자 상품 리스트</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>
<div class="list">
<div class="search-form">
<p class="total-count">전체 <span th:text="${totalCount}"></span>개 </p>
<form method="get">
<select name="searchType">
<option value="all">전체</option>
<!-- 상품을 검색하기 위한 수단 상품 이름 / 카테고리 / -->
<!-- 문자열이 아닌 형태는 어떻게 처리하지???-->
<option th:selected="${#strings.equals(param.searchType, 'productId')}" value="productId">상품
No
</option>
<option th:selected="${#strings.equals(param.searchType, 'productName')}"
value="productName">이름
</option>
<option th:selected="${#strings.equals(param.searchType, 'price')}" value="price">가격
</option>
</select>
<input th:value="${param.searchValue}" type="search" name="searchValue" placeholder="검색어 입력"/>
<button type="submit">검색</button>
</form>
</div>
<table>
<thead>
<tr>
<th>No</th>
<th>카테고리</th>
<th>상품 Id</th>
<th>상품명</th>
<th>가격</th>
<th>품절 여부</th>
<th>상품 사진</th>
<th>재고 수량</th>
<th>입고 예정일</th>
<th>누적 판매량</th>
</tr>
</thead>
<tbody>
<tr th:each="x : ${list}">
<td th:th:text="${x.seq}"></td>
<td th:th:text="${x.category}"></td>
<td th:text="${x.id}"></td>
<td th:text="${x.productName}"></td>
<td th:text="${x.price}"></td>
<td th:text="${x.soldOutYn}"></td>
<td th:text="${x.productImg}"></td>
<td th:text="${x.productQuantity}"></td>
<td th:text="${x.stockedDt}"></td>
<td th:text="${x.cumulativeSales}"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
Note) 실행 결과
Ex) delete 테스트
productService.delete(1L);
- delete () 실행 전
- delete () 실행 후
Ex) update 테스트
- test
<hide/>
ProductInput productInput = ProductInput.builder()
.id(3L)
.productName("목걸이")
.productQuantity(0)
.price(542)
.category(Category.CATEGORY_ACCESSORY)
.build();
productService.update(productInput);
- productServiceImpl
<hide/>
@Override
public boolean update(ProductInput parameter) { // id로 찾아서 다른 값들을 바꾼다.
Optional<Product> OptionalProduct = productRepository.findById(parameter.getId());
if (OptionalProduct.isPresent()) {
Product product = OptionalProduct.get();
product.setProductImg(parameter.getProductImg());
product.setProductName(parameter.getProductName());
product.setProductQuantity(parameter.getProductQuantity());
product.setCategory(parameter.getCategory());
product.setPrice(parameter.getPrice());
product.setStockedDt(parameter.getStockedDt());
productRepository.save(product);
return true;
}
return false;
}
- update() 실행 전
- update() 실행 후
점검 사항
- add(), delete() 는 확인 완료
- update() 구현부 마지막에 save()를 해줘야한다!
- 상품 추가, delete는 html에 내용 추가하기
- cart : product 는 일대다 연관 관계
- 그러면 cart하나에 여러 개의 product를 매핑 가능하다
- 장바구니는 곧 멤버를 의미한다.
'Spring Projcect > [갠플] Online-mall' 카테고리의 다른 글
[9일차] 관리자 API - 상품 등록, 수정, 삭제 (1) | 2022.10.04 |
---|---|
[8일차] Order 엔티티 매핑 (0) | 2022.10.03 |
[6일차] 관리자 로그인, 상품 등록 (0) | 2022.09.28 |
[5일차] 연관 관계 매핑 - 장바구니, 쿠폰 (0) | 2022.09.27 |
[4일차] 오류 해결, 로그인 히스토리 (0) | 2022.09.24 |