기존 코드를 작성하면서 @NoArgsConstructor로 생성자를 만들면서 access = PROTECTED를 도데체 왜 사용하는지 궁금해서 공부해보았다.
우선 - PUBLIC은 생성자를 너무 무분별하게 생성이 가능한 상황이 발생할 수 있기에 사용하지 않는 것을 추천하는 상황이다 .
그럼 남은 아이들이 PROTECTED와 PRIVATE인데 그럼 둘은 어디서 무엇이 다른걸까?
결론부터 보자면 PRIVATE은 아래의 에러를 발생시킨다는 것을 확인할 수 있다.
ERROR : Class 'StoreEntity' should have [public, protected] no-arg constructor
Entity Proxy가 원인이다.
아래 코드를 따라가 보자
FOOD ENTITY
@Entity
@NoArgsConstructor(access = PROTECTED/PRIVATE)
public class FoodEntity{
@Id
@GeneratedValue(strategy = GeneratedType.AUTO)
private Long foodId;
private String foodName;
@ManyToOne(fetch=FetchType.Lazy)
@JoinColumn(name = store_id)
public StoreEntity storeEntity
}
STORE ENTITY
@Entity
@NoArgsConstructor(access = PROTECTED/PRIVATE)
public class StoreEntity{
@Id
@GeneratedValue(strategy = GeneratedType.AUTO)
private Long storeId;
private String storeName;
}
그럼 두 엔티티
STORE ENTITY, FOOD ENTITY
PROTECTED, PRIVATE의 상황을 보겠다.
@SpringBootTest
public class ProxyTest {
@Autowired
private EntityManager em;
@Test
@Transactional
public void proxyTest(){
FoodEntity foodEntity = em.find(FoodEntity.class,5L);
//em.find : 실제 Foodentity 접근
System.out.println("Food ID : " +foodEntity.getFoodId());
System.out.println("Food Name : " + foodEntity.getFoodName());
System.out.println(foodEntity.getStoreEntity().getClass());
System.out.println("======= 쿼리 결과 =======");
System.out.println("======= Store 데이터 =======");
System.out.println("Store ID : " + foodEntity.getStoreEntity().getStoreId());
System.out.println("Store Name : " + foodEntity.getStoreEntity().getStoreName());
em.close();
}
}
결과
Food와 Store 모두 (access = AccessLevel.PROTECTED)
-> Success
-> Food : 실제 Entity
-> Store : HibernateProxy
===================================================
Food : (access = AccessLevel.PROTECTED) ,
Store : (access = AccessLevel.PRIVATE)
-> ERROR : HHH000143: Bytecode enhancement failed because no public,
protected or package-private default constructor was found for entity:
com.jpastudy.ms.domain.Entity.StoreEntity.
Private constructors don''t work with runtime proxies!
:: 기본적으로
@NoArgsConstructor은 JPA Entity Proxy로 스캔 및 생성을 진행한다.
Food 조회 시 Store은 Proxy 객체로 조회 되지만, Proxy 객체 생성을
@NoArgsConstructor(access = PIRIVATE)의 PIRIVATE으로 오류 발생
====================================================
Food : (access = AccessLevel.PRIVATE) ,
Store : (access = AccessLevel.PROTECTED)
-> Success
-> Food : 실제 Entity
-> Store : protected -> Proxy (o)
====================================================
Food와 Store 모두 (access = AccessLevel.PRIVATE)
-> ERROR
====================================================
Entity : @NoArgsConstructor(access = PIRIVATE) + EAGER
EAGER(즉시 로딩)이라면 문제 없음
실무에서 우리는 지연로딩- LAZY를 기본으로 깔고 들어간다.
지연 로딩의 상황에서 @Entity에 @NoArgsConstructor(access = PRIVATE)을 적용하게 된다면 Proxy가 접근하지 못하는 상황이 발생한다.
위의 오류를 방지하기 위해서 @NoArgsConstructor(access = PROTECTED)를 사용하는 상황이며 PUBLIC은 무분별한 생성자가 만들어지는 상황이 발생하기에 사용하지 않는다.
'개발 > JAVA' 카테고리의 다른 글
자바 flatMap 이해하기 (0) | 2022.11.03 |
---|---|
모던 자바 인 액션 스터디 (0) | 2022.10.13 |
ModelMapper와 친해지기 (0) | 2022.07.14 |
Iterator (0) | 2022.07.11 |
자바 8 정리 (0) | 2022.07.08 |