Product 엔티티
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long pno;
@ElementCollection
private List<ProductImage> imageList = new ArrayList<>();
ProductImage
@Embeddable // 임베디드 할 수 있다는 걸 체크!
@Getter
@ToString
@NoArgsConstructor
public class ProductImage {
private String fileName;
@Setter
private int ord;
@Builder
public ProductImage(String fileName, int ord) {
this.fileName = fileName;
this.ord = ord;
}
}
실제 DB 상의 필드
product_image 테이블
값 타입 컬렉션은 자신의 라이프 사이클을 가지지 않는다.
이것은 마치 영속성 전이(Cascade)와 고아 객체 제거 기능을 필수로 가진 것과 비슷하다고 볼 수 있다.
또한 기본적으로 @ElementCollection이 LAZY로 설정돼 있어 지연로딩으로 작동한다.
값 타입 컬렉션 안의 데이터를 수정할 때는 일부만 수정하는 것이 아닌 데이터를 삭제 후 변경된 데이터 전체를 새로 추가해줘야 한다.
부모엔티티인 Product 내 그 로직이 담겨져 있다.
Product 엔티티 내 imageList 추가/수정 메서드
public void addImage(ProductImage image) {
if(this.imageList == null) {
this.imageList = new ArrayList<>();
}
int size = imageList.size();
image.setOrd(size);
this.imageList.add(image);
}
public void addImageString(String fileName) {
ProductImage image = ProductImage.builder()
.fileName(fileName)
.build();
addImage(image);
}
public void clearList() {
this.imageList.clear();
}
서비스로직에서 사용시
@Override
public Long modify(ProductRequest request) {
// 조회
Product product = productRepository.findById(request.getPno())
.orElseThrow(() -> new RuntimeException("해당 상품이 없습니다. pno: " + request.getPno()));
// 변경 내용 반영
product.changeName(request.getPname());
product.changePrice(request.getPrice());
product.changeDesc(request.getPdesc());
product.changeDelFlag(request.isDelFlag());
// 이미지 처리
List<String> uploadedFileName = request.getUploadedFileName();
// 기존 이미지 삭제
product.clearList();
if (!CollectionUtils.isEmpty(uploadedFileName)) {
uploadedFileName.forEach(product::addImageString);
}
// 저장
Product savedProduct = productRepository.save(product);
return savedProduct.getPno();
}