본문 바로가기
개발/JAVA

@NoArgsConstructor(access = PROTECTED)에 관하여

by 설이주인 2022. 9. 12.

기존 코드를 작성하면서 @NoArgsConstructor로 생성자를 만들면서 access = PROTECTED를 도데체 왜 사용하는지 궁금해서 공부해보았다.

 

우선 - PUBLIC은 생성자를 너무 무분별하게 생성이 가능한 상황이 발생할 수 있기에 사용하지 않는 것을 추천하는 상황이다 .

 

그럼 남은 아이들이 PROTECTEDPRIVATE인데 그럼 둘은 어디서 무엇이 다른걸까?

 

결론부터 보자면 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