Spring boot & JPA

Spring Boot - 쇼핑몰 프로젝트 06 (상품 등록 및 조회하기 - 2)

록's 2023. 4. 6. 16:20
728x90
반응형

 

 

 

 


상품 등록 및 조회하기 - 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

 


 

 

728x90
반응형