[JPA] @MappedSuperclass vs Embedded Type

rudwnd33·2022년 2월 5일
0
post-thumbnail

JPA를 공부하면서 문득 든 생각이 있다.

@MappedSuperclass@Embeddable+@Embedded의 용도가 비슷한데
어떨 때 @MappedSuperclass를 쓰고, 어떨 때 @Embeddable+@Embedded를 쓸까?

@MappedSuperclass

  • 상속관계 매핑이 아니다.
  • 엔티티가 아니고, 테이블과 매핑되지 않는다.
  • 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공한다.
  • 직접 생성해서 사용할 일이 없으므로 추상 클래스로 사용하는 것이 좋다.
  • 테이블과 관계 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할이다.
  • 주로 등록일, 수정일, 등록한 사람, 수정한 사람 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용한다.

참고로 @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;
	...
}

Embedded Type

  • 새로운 값 타입을 직접 정의할 수 있다.
  • JPA는 임베디드 타입이라고 한다.
  • 주로 기본 값 타입을 모아서 만들기 때문에 복합 값 타입이라고도 한다.
  • 임베디드 타입은 엔티티가 아니다. 임베디드 타입은 단순히 값들을 하나로 묶어 놓은 것이다.

엔티티를 구현하다보면 공통적인 부분을 따로 클래스로 만들어 사용하고 싶은 경우가 있다. 분리한 클래스의 멤버변수를 테이블에 매핑하고 싶을 때 @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;
	...
}

결론

결국 상속을 사용하는 것(@Embeddable)과 위임을 사용하는 것(Embedded Type)의 차이다.

  • 부모 타입과 자식 타입이 강한 결합성을 가짐으로써 캡슐화가 깨지고, 속한 객체들이 변경의 여파를 받게 된다.
  • 한 객체에서 정의된 상태나 행위에 대해서 관리하거나 확인해야 할 포인트(class)가 늘어난다.
  • 자식 타입에서 메서드를 재정의하는 경우에 잘못된 이해로 특정 로직만 추가하고 부모 타입의 메서드를 호출하고 있을 때 오동작할 수 있다.
    부모 타입의 메서드가 변경되어서 자식 타입의 메서드가 깨지거나 오동작할 수 있다.

등의 문제점들을 미루어보았을 때

부모 타입의 기능 혹은 다른 객체의 기능을 사용할 것이라면 이를 상속받는 것이 아니라 상태 값, 즉 인스턴스 변수를 통해 기능을 사용하는 것을 추천하게 되는 것이다.

따라서 객체지향의 일반적인 법칙을 따르면 상속보다는 위임(조합)이 더 좋기 때문에 위임을 사용한다.

하지만 등록일, 수정일, 등록한 사람, 수정한 사람과 같이 운영상의 이유를 포함하는 컬럼을 공통으로 사용할 때는 상속을 사용하는게 더욱 편리하다.

예를 들어, 이런 경우에서 JPQL을 사용하면 다음과 같이 항상 아래 코드의 timestamped와 같이 임베디드 타입을 적어야 한다.

select u from User u where u.timestamped.createdDate > ?

상속을 사용하면 다음과 같이 간단하고 쉽게 풀린다.

select u from User u where u.createdDate > ?

결국 두 가지 중 선택이지만, 편리함직관성 때문에, 이 경우에는 상속(@MappedSuperclass)을 사용한다고 한다.

참고

profile
주니어 백엔드 개발자

1개의 댓글

comment-user-thumbnail
2022년 2월 8일

넘 어려워요

답글 달기