JPA를 공부하면서 문득 든 생각이 있다.
@MappedSuperclass
와@Embeddable
+@Embedded
의 용도가 비슷한데
어떨 때@MappedSuperclass
를 쓰고, 어떨 때@Embeddable
+@Embedded
를 쓸까?
참고로 @Entity
클래스는 엔티티나 @MappedSuperclass
로 지정한 클래스만 상속이 가능하다.
@Getter
@Setter
@MappedSuperclass
public abstract class Timestamped {
private LocalDateTime createdDate;
private LocalDateTime modifiedDate;
}
@Entity
public class User extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "USER_ID")
private Long id;
@Column(nullable = false)
private String username;
...
}
엔티티를 구현하다보면 공통적인 부분을 따로 클래스로 만들어 사용하고 싶은 경우가 있다. 분리한 클래스의 멤버변수를 테이블에 매핑하고 싶을 때 @Embeddable
+@Embedded
을 사용한다.
@Embeddable
: 값 타입을 정의하는 곳에 표시한다.@Embedded
: 값 타입을 사용하는 곳에 표시한다.@Getter
@Setter
@Embeddable
public class Timestamped {
private LocalDateTime createdDate;
private LocalDateTime modifiedDate;
}
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "USER_ID")
private Long id;
@Column(nullable = false)
private String username;
@Embedded
private Timestamped timestamped;
...
}
결국 상속을 사용하는 것과 위임을 사용하는 것의 차이다.
등의 문제점들을 미루어보았을 때
부모 타입의 기능 혹은 다른 객체의 기능을 사용할 것이라면 이를 상속받는 것이 아니라 상태 값, 즉 인스턴스 변수를 통해 기능을 사용하는 것을 추천하게 되는 것이다.
따라서 객체지향의 일반적인 법칙을 따르면 상속보다는 위임(조합)이 더 좋기 때문에 위임을 사용한다.
하지만 등록일, 수정일, 등록한 사람, 수정한 사람과 같이 운영상의 이유를 포함하는 컬럼을 공통으로 사용할 때는 상속을 사용하는게 더욱 편리하다.
예를 들어, 이런 경우에서 JPQL을 사용하면 다음과 같이 항상 아래 코드의 timestamped와 같이 임베디드 타입을 적어야 한다.
select u from User u where u.timestamped.createdDate > ?
상속을 사용하면 다음과 같이 간단하고 쉽게 풀린다.
select u from User u where u.createdDate > ?
결국 두 가지 중 선택이지만, 편리함과 직관성 때문에, 이 경우에는 상속(@MappedSuperclass
)을 사용한다고 한다.
참고
넘 어려워요