| 스프링 부트 Test 중, 아래와 같이 오류가 나타났다.
C:\sebinSample\cms\order-api\src\main\java\org\zerobase\cms\order\domain\model\Product.java:33: warning: @Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.
private List<ProductItem> productItems = new ArrayList();
^
Note: C:\sebinSample\cms\order-api\src\main\java\org\zerobase\cms\order\domain\model\Product.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
- 테스트 코드
@Test
void addProduct() {
// given
Long sellerId = 1L;
AddProductForm form = makeProductForm("나이키 에어포스", "신발", 3);
// when
Product p = productService.addProduct(sellerId, form);
// then
Product result = productRepository.findById(p.getId()).get();
Assertions.assertNotNull(result);
Assertions.assertEquals(result.getSellerId(), 1L);
Assertions.assertEquals(result.getName(), "나이키 에어포스");
Assertions.assertEquals(result.getDescription(), "신발");
Assertions.assertEquals(result.getProductItems().get(0).getName(), "나이키 에어포스0");
Assertions.assertEquals(result.getProductItems().get(0).getPrice(), 10000);
}
private static AddProductForm makeProductForm(String name, String description, int itemCount) {
List<AddProductItemForm> addProductItemForms = new ArrayList<>();
for (int i = 0; i < itemCount; i++) {
addProductItemForms.add(makeProductItemForm(null, name + i));
}
return AddProductForm.builder()
.name(name)
.description(description)
.addProductItemForms(addProductItemForms)
.build();
}
> 원인 :
- @OneToMany의 default fetch type이 LazyLoading이기 때문에, Proxy로 id 값만 담은 ProductItem들을 가져오고, 실제 내용은 들어있지 않았기 때문이다.
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@AuditOverride(forClass = BaseEntity.class)
@Audited // Entity가 변할 때마다, 변화된 내용을 저장
public class Product extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long sellerId;
private String name;
private String description;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "product_id")
private List<ProductItem> productItems = new ArrayList();
public static Product of(Long sellerId, AddProductForm form) {
return Product.builder()
.sellerId(sellerId)
.name(form.getName())
.description(form.getDescription())
.productItems(form.getAddProductItemForms().stream()
.map(p -> ProductItem.of(sellerId, p)).collect(Collectors.toList())
).build();
}
}
> 해결 :
이를 해결하기 위해서는 두가지 방법을 사용할 수 있는데
(1) fetch type을 EAGER로 변경한다. --> 이 방법은 그러나 불필요하게 매번 DB 조회 시 모든 데이터를 한 번에 가져오게 함으로 좋지 않다.
(2) JPA의 @EntityGraph와 findWith 함수를 통해 속성을 지정(ex. productItems)할 때, fetch type을 변경시킨다.
EntityGraphType.LOAD | attributePaths가 지정된 경우 EAGER로, 지정되지 않으면 default fetch type으로 |
EntityGraphType.FETCH | attributePaths가 지정된 경우 EAGER로, 지정되지 않으면 LAZY로 |
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
@EntityGraph(attributePaths = {"productItems"}, type = EntityGraph.EntityGraphType.LOAD)
Optional<Product> findWithProductItemsById(Long id);
}
[ 출처 ]
부트캠프 수업 내용 정리
'Framework > Orm&Mapper' 카테고리의 다른 글
[JPA] CascadeType : 상위 -> 하위 엔터티 전파 타입 (0) | 2022.11.28 |
---|---|
[JPA] Lazy Loading 개념 (0) | 2022.11.11 |
[Entity] 중복데이터 저장 방지 (Unique Key, Index) (0) | 2022.11.11 |
[Transaction] Transaction에 대한 이해 (& 스프링의 @Transactionl) (1) | 2022.10.25 |
[JPA] 인텔리J에서 JPA Entity 기반 ERD 그리기 (0) | 2022.10.19 |