JPA를 공부하면서 Entity에는 기본생성자가 꼭 필요하다는 것을 알게되었다.
방법은 @NoArgsConstructor(access = AccessLevel.PROTECTED)와 같은 어노테이션을 활용하여 접근을 제한하고 자동으로 기본 생성자가 생성되도록 하면 된다.
생성자를 쓰는 이유는 setter의 무분별한 사용을 막아 entity의 일관성을 유지하기 위함인데
그렇다면 entity에는 적어도 하나 이상의 field들이 있을 것이고 이를 반영하려면 매게변수가 여러개인 생성자가 필요로 할 것인데, 굳이 왜 기본 생성자가 필요로 한 것 일까?
궁금해서 관련 자료를 찾아보았다.
Java Reflection
구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API.
정의를 보니 한 가지 의문점이 들었다. 자신이 작성한 코드인데 구체적인 클래스 타입을 알지 못하는 경우가 있나?
하지만 그런 경우가 존재한다고 한다. 예를 들면 코드를 작성할 시점에는 어떤 타입의 클래스를 사용할지 모르지만, 런타임 시점에 실행되고 있는 클래스를 실행해야 하는 경우와 같을 때 이다.
이를 활용하면 클래스 타입을 모르는 상태에서 로딩이 완료된 클래스에서 다른 클래스를 동적으로 로딩하여 생성자, 멤버 필드, 멤버 메서드 등을 사용 할 수 있다.
하지만 Java Reflection이 가져올 수 없는 정보가 생성자의 인자 정보들이라, 기본 생성자가 없다면 Java Reflection은 객체를 생성할 수 없게 된다.
JPA entity또한 이와 마찬가지인데, entity에서 기본 생성자가 필요한 이유는 JPA가 동적으로 객체 생성을 할 때 위와 같은 Reflection을 사용하기 때문이다.
기본 생성자가 필요하다는 것을 안 후에 쇼핑몰 프로젝트를 위해 만든 entity들을 다시 한번 살펴보았다.
그런데 해당 entity에는 기본생성자가 별도로 명시되어 있지 않았는데 정상으로 동작하였다. 그 이유는 뭘까?
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order extends BaseEntity {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
private LocalDateTime orderDate; //주문일
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus; //주문상태
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL
, orphanRemoval = true, fetch = FetchType.LAZY)
private List<OrderItem> orderItems = new ArrayList<>();
(작성한 entity 코드인데 기본 생성자가 없어도 정상 작동한다)
조금 더 알아본 결과, 위와 같은 경우는 다른 생성자가 없기 때문에 자동으로 기본생성자가 적용되는 것으로 보인다.
만약, 다른 인수들을 활용하여 새로운 생성자를 만들 경우 기본생성자 에러가 발생할 수 있으므로
@NoArgsConstructor(access = AccessLevel.PROTECTED)와 같은 어노테이션이 필요할 것으로 보인다.