Spring Boot - 쇼핑몰 프로젝트 06 (상품 등록 및 조회하기 - 2)
상품 등록 및 조회하기 - 2
상품 수정하기
- 상품 등록 후 콘솔창을 보면 insert into item 쿼리문에서 item_id에 들어가는 binding parameter 값 확인
- 해당 상품 아이디를 이용해서 상품 수정 페이지에 진입 예제 진행
상품 수정하기
com.shop.service.ItemService.java
// ItemService.java
package com.shop.service;
import com.shop.dto.ItemFormDto;
import com.shop.dto.ItemImgDto;
import com.shop.entity.Item;
import com.shop.entity.ItemImg;
import com.shop.repository.ItemImgRepository;
import com.shop.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
private final ItemImgService itemImgService;
private final ItemImgRepository itemImgRepository;
public Long saveItem(ItemFormDto itemFormDto, List<MultipartFile> itemImgFileList) throws Exception {
// 상품 등록
Item item = itemFormDto.createItem();
itemRepository.save(item);
// 이미지 등록
for(int i=0; i<itemImgFileList.size(); i++) {
ItemImg itemImg = new ItemImg();
itemImg.setItem(item);
if(i == 0)
itemImg.setRepimgYn("Y");
else
itemImg.setRepimgYn("N");
itemImgService.saveItemImg(itemImg, itemImgFileList.get(i));
}
return item.getId();
}
@Transactional(readOnly = true)
public ItemFormDto getItemDtl(Long itemId){
List<ItemImg> itemImgList =
itemImgRepository.findByItemIdOrderByIdAsc(itemId);
List<ItemImgDto> itemImgDtoList = new ArrayList<>();
for(ItemImg itemImg : itemImgList) {
ItemImgDto itemImgDto = ItemImgDto.of(itemImg);
itemImgDtoList.add(itemImgDto);
}
Item item = itemRepository.findById(itemId)
.orElseThrow(EntityNotFoundException::new);
ItemFormDto itemFormDto = ItemFormDto.of(item);
itemFormDto.setItemImgDtoList(itemImgDtoList);
return itemFormDto;
}
}
com.shop.controller.ItemController.java
// ItemController.java
... 생략 ...
@GetMapping(value ="/admin/item/{itemId}")
public String itemDtl(@PathVariable("itemId") Long itemId, Model model){
try {
ItemFormDto itemFormDto = itemService.getItemDtl(itemId);
model.addAttribute("itemFormDto", itemFormDto);
} catch(EntityNotFoundException e) {
model.addAttribute("errorMessage", "존재하지 않는 상품 입니다.");
model.addAttribute("itemFormDto", new ItemFormDto());
return "item/itemForm";
}
return "item/itemForm";
}
}
- 상품 아이디는 insert into item 쿼리문에서 각자 확인한 상품 아이디 입력
- 저장한 상품을 조회하기 위해서 웹 브라우저에 http://localhost/admin/item/{상품번호} 입력
- 상품 아이디가 2번으로 발급됐을 경우 localhost/admin/item/2 입력
com.shop.service.ItemImgService.java
// ItemImgService.java
... 기존 임포트 생략 ...
import javax.persistence.EntityNotFoundException;
... 생략 ...
public void updateItemImg(Long itemImgId, MultipartFile itemImgFile) throws Exception {
if(!itemImgFile.isEmpty()) {
ItemImg savedItemImg = itemImgRepository.findById(itemImgId)
.orElseThrow(EntityNotFoundException::new);
// 기존 이미지 파일 삭제
if (!StringUtils.isEmpty(savedItemImg.getImgName())) {
fileService.deleteFile(itemImgLocation + "/" + savedItemImg.getImgName());
}
String oriImgName = itemImgFile.getOriginalFilename();
String imgName = fileService.uploadFile(itemImgLocation, oriImgName, itemImgFile.getBytes());
String imgUrl = "/images/item/" + imgName;
savedItemImg.updateItemImg(oriImgName, imgName, imgUrl);
}
}
}
com.shop.entity.Item.java
// Item.java
... 생략 ...
public void updateItem(ItemFormDto itemFormDto) {
this.itemNm = itemFormDto.getItemNm();
this.price = itemFormDto.getPrice();
this.stockNumber = itemFormDto.getStockNumber();
this.itemDetail = itemFormDto.getItemDetail();
this.itemSellStatus = itemFormDto.getItemSellStatus();
}
}
com.shop.service.ItemService.java
// ItemService.java
... 생략 ...
public Long updateItem(ItemFormDto itemFormDto, List<MultipartFile> itemImgFileList) throws Exception {
// 상품 수정
Item item = itemRepository.findById(itemFormDto.getId())
.orElseThrow(EntityNotFoundException::new);
item.updateItem(itemFormDto);
List<Long> itemImgIds = itemFormDto.getItemImgIds();
//이미지 등록
for(int i=0; i<itemImgFileList.size(); i++){
itemImgService.updateItemImg(itemImgIds.get(i), itemImgFileList.get(i));
}
return item.getId();
}
}
com.shop.controller.ItemController.java
// ItemController.java
... 생략 ...
@PostMapping(value ="/admin/item/{itemId}")
public String itemUpdate(@Valid ItemFormDto itemFormDto,BindingResult bindingResult,
@RequestParam("itemImgFile") List<MultipartFile> itemImgFileList, Model model) {
if(bindingResult.hasErrors()) {
return "item/itemForm";
}
if(itemImgFileList.get(0).isEmpty() && itemFormDto.getId() == null) {
model.addAttribute("errorMessage", "첫번째 상품 이미지는 필수 입력 값 입니다.");
return "item/itemForm";
}
try {
itemService.updateItem(itemFormDto, itemImgFileList);
} catch (Exception e){
model.addAttribute("errorMessage", "상품 수정 중 에러가 발생하였습니다.");
}
return "redirect:/";
}
}
- 상품 수정 시 데이터가 변경되는 것을 확인 가능
상품관리하기
- 상품 관리 화면에서는 상품을 조회하는 조건을 설정 후 페이징 기능을 통해 일정 개수의 상품만 불러오며, 선택한 상품 상세 페이지로 이동할 수 있는 기능까지 구현
- 조회 조건
- 상품 등록일
- 상품 판매 상태
- 상품명 또는 상품 등록자 아이디
- 조회 조건이 복잡한 경우 Querydsl을 이용해 조건에 맞는 쿼리를 동적으로 쉽게 생성
- Querydsl을 사용하면 비슷한 쿼리를 재활용할 수 있음
- 자바 코드로 작성하기 때문에 IDE의 도움을 받아서 문법 오류를 바로 수정 가능
- QDomain을 생성하기 위해서 메이븐의 컴파일 명령 실행
상품 관리 메뉴 구현하기
com.shop.dto.ItemSearchDto.java
// ItemSearchDto.java
package com.shop.dto;
import com.shop.constant.ItemSellStatus;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ItemSearchDto {
private String searchDateType;
private ItemSellStatus searchSellStatus;
private String searchBy;
private String searchQuery = "";
}
- Querydsl을 Spring Data Jpa과 함께 사용하기 위해서 사용자 정의 리포지토리를 정의. 총 3단계의 과정으로 구현
- 사용자 정의 인터페이스 작성
- 사용자 정의 인터페이스 구현
- Spring Data Jpa 리포지토리에서 사용자 정의 인터페이스 상속
- 사용자 정의 인터페이스 작성
com.shop.repository.ItemRepositoryCustom.java
// ItemRepositoryCustom.java
package com.shop.repository;
import com.shop.dto.ItemSearchDto;
import com.shop.entity.Item;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface ItemRepositoryCustom {
Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable);
}
- 사용자 정의 인터페이스 구현
com.shop.repository.ItemRepositoryCustomImpl.java
// ItemRepositoryCustomImpl.java
package com.shop.repository;
import com.querydsl.core.QueryResults;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.shop.constant.ItemSellStatus;
import com.shop.dto.ItemSearchDto;
import com.shop.entity.Item;
import com.shop.entity.QItem;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.thymeleaf.util.StringUtils;
import javax.persistence.EntityManager;
import java.time.LocalDateTime;
import java.util.List;
public class ItemRepositoryCustomImpl implements ItemRepositoryCustom {
private JPAQueryFactory queryFactory;
public ItemRepositoryCustomImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
private BooleanExpression searchSellStatusEq(ItemSellStatus searchSellStatus) {
return searchSellStatus == null ? null : QItem.item.itemSellStatus.eq(searchSellStatus);
}
private BooleanExpression regDtsAfter(String searchDateType) {
LocalDateTime dateTime = LocalDateTime.now();
if (StringUtils.equals("all", searchDateType) ||searchDateType == null) {
return null;
} else if(StringUtils.equals("1d", searchDateType)) {
dateTime = dateTime.minusDays(1);
} else if(StringUtils.equals("1w", searchDateType)) {
dateTime = dateTime.minusWeeks(1);
} else if(StringUtils.equals("1m", searchDateType)) {
dateTime = dateTime.minusMonths(1);
} else if(StringUtils.equals("6m", searchDateType)) {
dateTime = dateTime.minusMonths(6);
}
return QItem.item.regTime.after(dateTime);
}
private BooleanExpression searchByLike(String searchBy, String searchQuery) {
if(StringUtils.equals("itemNm", searchBy)) {
return QItem.item.itemNm.like("%" + searchQuery + "%");
} else if (StringUtils.equals("createdBy", searchBy)) {
return QItem.item.createdBy.like("%" + searchQuery + "%");
}
return null;
}
@Override
public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
QueryResults<Item> results = queryFactory
.selectFrom(QItem.item)
.where(regDtsAfter(itemSearchDto.getSearchDateType()),
searchSellStatusEq(itemSearchDto.getSearchSellStatus()),
searchByLike(itemSearchDto.getSearchBy(), itemSearchDto.getSearchQuery()))
.orderBy(QItem.item.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetchResults();
List<Item> content = results.getResults();
long total = results.getTotal();
return new PageImpl<>(content, pageable, total);
}
}
- Spring Data Jpa 리포지토리에서 사용자 정의 인터페이스 상속
com.shop.repository.ItemRepository.java
// ItemRepository.java
... 생략 ...
public interface ItemRepository extends JpaRepository<Item, Long>, QuerydslPredicateExecutor<Item>,
ItemRepositoryCustom {
... 생략 ...
}
com.shop.service.ItemService.java
// ItemService.java
... 기존 임포트 생략 ...
import com.shop.dto.ItemFormDto;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
... 생략 ...
@Transactional(readOnly = true)
public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
return itemRepository.getAdminItemPage(itemSearchDto, pageable);
}
}
com.shop.controller.ItemController.java
// ItemController.java
... 기존 임포트 생략 ...
import com.shop.dto.ItemSearchDto;
import com.shop.entity.Item;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
... 생략 ...
@GetMapping(value = {"/admin/items", "/admin/items/{page}"})
public String itemManage(ItemSearchDto itemSearchDto,
@PathVariable("page") Optional<Integer> page, Model model) {
Pageable pageable = PageRequest.of(page.isPresent() ? page.get() : 0, 3);
Page<Item> items = itemService.getAdminItemPage(itemSearchDto, pageable);
model.addAttribute("items", items);
model.addAttribute("itemSearchDto",itemSearchDto);
return "item/itemMng";
}
}
resources/templates/item/itemMng.html
https://rogi221.tistory.com/178
ItemForm.html - github
https://rogi221.tistory.com/177 깃허브에서 자료 복사해서 입력 // ItemFrom.html 상품 등록 판매중 품절 상품명 Incorrect data 가격 Incorrect data 재고 Incorrect data 상품 상세 내용 Incorrect data 저장 수정
rogi221.tistory.com