본인은 회사에서도 그렇고, 개발할 때 최근에는 주로 NoSQL을 사용하고 있다.
(그리고 RBDMS를 사용하더라도 JPA를 사용하고있지는 않아서 JPA는 문외한이기도 하다)
최근 사이드 프로젝트를 진행하는데, 팀원이 Document 클래스에 @Builder
, @RequiredArgsConstructor
, @NoArgsConstructor
을 주렁주렁 달아둔 것을 보고 실제로는 빌더만 사용하니 나머지는 지워도 될 듯 하다고 피드백한 적이 있다.
그런데 되려 왜 지워도 되냐는 질문을 받았고, 내친김에 이에 대해 찾아보게 되었다.
Mongo에서는 아래같이 빌더만 사용해도 잘 작동하는데, MySQL에서는 안되는 이유가 무엇일까?
// MongoDB
@Getter
@Builder
@Document
public class Member {
}
// MySQL
@Getter
@Builder
@RequiredArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {
}
@XArgsConstructor
롬복의 생성자 어노테이션은 다음과 같이 3가지가 있다.
@NoArgsConstructor
: 파라미터가 없는 디폴트 생성자 생성@AllArgsConstructor
: 모든 필드를 받는 생성자 생성@RequiredArgsConstructor
: 필수(final) 필드만 받는 생성자 생성 그리고 이 생성자 어노테이션에 속한 속성은 다음과 같다.
staticName
: @Data의 staticConstructor 속성과 같은 역할을 한다. (=static한 생성자를 만들어준다.)access
: 'PUBLIC', 'MODULE', 'PROTECTED', 'PACKAGE', 'PRIVATE' 의 값으로 접근 제한자를 설정할 수 있다.onConstructor
: 생성자에 어노테이션을 작성할 수 있다.force
: NoArgsConstructor
에만 존재하는 속성으로, final 등 필수 필드를 0/null/false 로 강제 초기화는 기본생성자를 생성한다.결론을 말하자면, RDBMS가 아니라 JPA와 관련된 문제이다.
JPA 에서는 영속성 컨텍스트를 만드려면 Entity는 public / protected 레벨의 생성자를 가지고 있어야한다.
@Builder
는 package-private 레벨의 생성자를 만들기 때문에, jpa를 사용할 경우 해당 레벨의 다른 생성자가 없으면 에러가 발생하는 것이다.
그렇다고 냅다 @NoArgsConstructor
만 추가해버리면 빌더에서 컴파일 에러가 발생한다.
@AllArgsConstructor
를 추가하면 해결되는 것은 알고 있었는데, 그 이유에 대해서도 찾아보았다.
@NoArgsConstructor
만 추가했을 시 발생하는 에러 메세지는 다음과 같다.
모든 멤버변수를 받는 생성자가 없기 때문이다.
롬복 빌더는 특정 매개변수를 받는 생성자든, 롬복 생성자든 정의한 생성자가 있는 상황이라면 그 중 @Builder
어노테이션이 붙어있는 생성자를 사용하게 된다.
생성자에 @Builder
를 붙이지 않고 클래스에 @Builder
를 붙인 경우, 기본적으로 Lombok은 @AllArgsConstructor(access = AccessLevel.PACKAGE)
를 만들고, 그 위에 @Builder
를 붙였다고 간주하고 빌더를 만든다.
Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the @Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself.
하지만 클래스 내에 특정 생성자가 있다면, 롬복은 암묵적으로 @AllArgConstructor
를 만들지 않는다.
따라서 @NoArgsConstructor
를 정의하고, 클래스 위치에 @Builder 를 붙인 상황이라면,
빌더는 모든 매개변수를 받는 생성자를 호출하게 되는데 실제로는 모든 매개변수를 받는 생성자가 없는 상황이 발생하게 된다.
해결책으로는
1. 클래스 위에 @AllArgsConstructor
를 이용해서 모든 매개변수를 받는 생성자를 정의하던지
2. 클래스 내에 특정 생성자 또는 팩토리 패턴의 객체 생성 메서드를 정의 후, @Builder
를 추가해야한다.
Ref.
https://projectlombok.org/api/lombok/Builder