등록된 상품을 불러오는 메소드를 추가합니다.
package me.jincrates.gobook.service;
//...기존 임포트 생략
import me.jincrates.gobook.web.dto.ItemImgDto;
import org.springframework.web.multipart.MultipartFile;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
@Transactional
@Service
public class ItemService {
//..기존 코드 생략
@Transactional(readOnly = true)
public ItemFormDto getItemDetail(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;
}
}
@Transactional(readOnly = true)
: 상품 데이터를 읽어오는 트랜잭션을 읽기 전용으로 설정합니다. 이럴 경우 JPA가 더티체킹(변경감지)을 수행하지 않아서 성능을 향상 시킬 수 있습니다.상품 수정 페이지로 진입하기 위해서 ItemController 클래스에 코드를 추가하겠습니다.
package me.jincrates.gobook.web;
//...기존 임포트 생략
import javax.persistence.EntityNotFoundException;
import javax.validation.Valid;
import java.util.List;
@RequiredArgsConstructor
@Controller
public class ItemController {
//...기존 코드 생략
@GetMapping(value = "/admin/item/{itemId}")
public String itemDetail(@PathVariable("itemId") Long itemId, Model model) {
try {
ItemFormDto itemFormDto = itemService.getItemDetail(itemId);
model.addAttribute("itemFormDto", itemFormDto);
} catch (EntityNotFoundException e) {
model.addAttribute("errorMessage", "존재하지 않는 상품입니다.");
model.addAttribute("itemFormDto", new ItemFormDto());
return "item/itemForm";
}
return "item/itemForm";
}
}
상품 이미지 데이터를 수정할 때는 변경감지 기능을 사용하겠습니다.
package me.jincrates.gobook.service;
//..기존 임포트 생략
import javax.persistence.EntityNotFoundException;
@RequiredArgsConstructor
@Transactional
@Service
public class ItemImgService {
//..기존 코드 생략
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);
}
}
}
itemImgRepository.findById(itemImgId)
: 상품 이미지 아이디를 이용하여 기존에 저장했던 상품 이미지 엔티티를 조회합니다.!StringUtils.isEmpty(savedItemImg.getImgName())
: 기존에 등록된 상품 이미지 파일이 있을 경우 해당 파일을 삭제합니다.fileService.uploadFile(itemImgLocation, oriImgName, itemImgFile.getBytes())
: 업데이트한 상품 이미지 파일을 업로드합니다.savedItemImg.updateItemImg(oriImgName, imgName, imgUrl)
: 변경된 상품 이미지 정보를 셋팅해줍니다. 여기서 중요한 점은 상품 등록 때처럼 itemImgRepository.save() 로직을 호출하지 않는다는 것입니다. savedItemImg 엔티티는 현재 영속 상태이므로 데이터를 변경하는 것만으로 변경 감지 기능이 동작하여 트랜잭션이 끝날 때 update 쿼리가 실행됩니다. 여기서 중요한 것은 엔티티가 영속 상태여야 한다는 것입니다.이제 상품을 업데이트하는 로직을 구현하겠습니다. 먼저 Item클래스에 상품 데이터를 업데이트하는 로직을 만들겠습니다. 엔티티 클래스에 비즈니스 로직을 추가한다면 조금 더 객체지향적으로 코딩을 할 수 있고, 코드를 재활용할 수 있습니다. 또한 데이터 변경 포인트를 한군데에서 관리할 수 있습니다.
package me.jincrates.gobook.domain.items;
//..기존 임포트 생략
@Getter @ToString
@NoArgsConstructor
@Table(name = "item")
@Entity
public class Item extends BaseEntity {
//...코드 생략
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();
}
}
상품을 업데이트할 때도 마찬가지로 변경 감지 기능을 사용합니다.
package me.jincrates.gobook.service;
//...기존 임포트 생략
@RequiredArgsConstructor
@Transactional
@Service
public class ItemService {
//..기존 코드 생략
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, max = itemImgFileList.size(); i < max; i++) {
itemImgService.updateItemImg(itemImgIds.get(i), itemImgFileList.get(i));
}
return item.getId();
}
}
item.updateItem(itemFormDto)
: 상품 등록 화면으로부터 전달 받은 ItemFormDto를 통해서 상품 엔티티를 업데이트 합니다.상품을 수정하는 URL을 추가하겠습니다. 상품 등록 때 추가했던 코드와 거의 비슷합니다.
package me.jincrates.gobook.web;
//..기존 임포트 생략
@RequiredArgsConstructor
@Controller
public class ItemController {
//..코드 생략
@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.saveItem(itemFormDto, itemImgFileList);
} catch (Exception e) {
model.addAttribute("errorMessage", "상품 수정 중 에러가 발생하였습니다.");
return "item/itemForm";
}
return "redirect:/";
}
}
상품이 정상적으로 수정되는지 확인하려고 하였으나....수정한 만큼 이미지 리스트가 쌓이네요....
하하 어쩐지 잘풀리더라ㅎㅎㅎ 어디가 잘못된건지 찾고 오겠습니다.