본문 바로가기
코딩 공부/web & Java

[JPA] @Entity는 기본 생성자를 왜 가져야 하는가?

by 현장 2023. 11. 22.

@Entity는 기본 생성자를 왜 가져야 하는가?

 

@Entity(name = "user_datails")
public class User {
    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    private LocalDate birthDate;

    @OneToMany(mappedBy = "user")
    private List<Post> posts;

    public User(Integer id, String name, LocalDate birthDate) {
        this.id = id;
        this.name = name;
        this.birthDate = birthDate;
    }

    public User() {

    }

   // getter, setter, toString 생략
}

 

위와 같이 조회 시 사용되는 엔티티(Entity)는 기본생성자를 가지고 있어야 하는데 그 이유를 알아봅시다.

 

🏷️ Reflection

EntityManger가 조회(find)를 요청하면 1차 캐시에 원하는 엔티티가 없는 경우 DB에 SELECT문을 실행하여 조회합니다. 그리고 결과를 1차 캐시에 저장하고 엔티티 객체를 생성하여 반환(return)합니다. 즉, 프로그램 실행 중에 동적으로 엔티티 객체를 생성해야 합니다.

 

이때 동적으로 생성하는 방법이 2가지가 있습니다.

1. new 연산자

2. Reflection

 

위와 같이 2가지 방법 중 JPA는 동적으로 객체를 생성할 때 new 연산자가 아닌 Reflection을 사용합니다. Reflection을 사용하는 이유는 '문자열'로 클래스를 탐색하여 객체를 생성하기에 컴파일 단계에서 에러를 뱉지 않습니다. 하지만 new 연산자는 원하는 클래스를 import 하지 않으면 컴파일과정에서 에러(can not find symbol)가 발생하기 때문입니다. 

import java.lang.reflect.Constructor;
 
public class ReflectionMain {
    public static void main(String[] args) {
 
        try {
            Class<?> clazz = Class.forName("org.example.reflection.user"); // 문자열로 클래스 탐색
            Constructor<?> constructor = clazz.getConstructor(); // 탐색된 클래스의 생성자 호출
            Object object = constructor.newInstance(); // 호출된 생성자로 동적으로 객체생성
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

위와 같이 원하는 클래스를 '문자열'로("org.example.reflection.user") 탐색하기에 원하는 클래스파일을 import하지 않아도 컴파일 과정에서 에러가 발생하지 않습니다. 

 

이를 통해서 컴파일 단계에서 발생가능한 에러를 런타임 단계로 미루게 되면서 엄청난 유연성을 제공하게 됩니다.

 

JPA는 엔티티를 자동으로 생성하여 클라이언트에 넘겨주어야 하는데 엔티티는 계속 새로 추가되고 컴파일 단계마다 엔티티를 관리해주어야 한다면 비효율일 수밖에 없습니다. 하지만 Reflection은 엔티티가 계속 추가되어도 코드 수정없이 엔티티를 동적으로 생성할 수 있습니다. 생성 후에는 클라이언트가 원하는 엔티티 클래스타입을 넘겨주면 JPA가 엔티티를 탐색하게 됩니다. 

 

 

entityManager.find(User.class,"user")
// 클라이언트는 Reflection이 엔티티 클래스를 탐색할 수 있도록 
// find 메소드에 파라미터로 클래스타입을 넣어주어야 합니다.

🏷️ 정리

https://docs.jboss.org/hibernate/core/3.3/reference/ko-KR/html/persistent-classes.html

JPA를 구현하는 Hibernate는 Reflection을 이용하여 객체를 동적으로 생성하는데, constructor.newInstance()로 객체를 생성합니다. 그러므로 엔티티는 아규먼트 없는 기본생성자를 무조건 가지고 있어야 하는  대신 Reflection은 클래스의 메타데이터에 접근하여 필드에 직접 데이터를 주입합니다.

Reflection은 기본생성자로 객체를 생성하고 필드에는 메타데이터와 어노테이션을 분석하여 테이블의 컬럼과 매핑되는 데이터를 저장합니다. 그리고 클라이언트에게 엔티티를 반환하게 됩니다.

 

이처럼 JPA는 Reflection을 사용하여 객체를 동적으로 생성하므로 어노테이션부터 동적쿼리생성까지 JPA에는 Reflection 개념이 사용되는 부분이 많아 상당히 중요한 개념입니다.

 

📖 Reference

L.O.K

'코딩 공부 > web & Java' 카테고리의 다른 글

[Spring / Web] CORS  (1) 2023.12.01
[WEB] MPA와 SPA  (0) 2023.11.25
[Spring] Response Customizing  (1) 2023.11.20
[Spring] HAL(Json Hypertext Application Language)  (1) 2023.11.19
[Spring] Actuator  (0) 2023.11.16