[JPA] @MappedSuperclass

olsohee·2023년 9월 26일
0

JPA

목록 보기
19/21
post-custom-banner

1. @MappedSuperclass

상속 관계 매핑은 부모 클래스와 자식 클래스 모두 데이터베이스 테이블과 매핑된다. 부모 클래스는 테이블과 매핑되지 않고, 자식 클래스에서 매핑 정보만 제공하고 싶으면 @MappedSuperclass를 사용하면 된다. @MappedSuperclass가 붙은 클래스는 실제 테이블과 매핑되지 않고, 자식 클래스에게 매핑 정보를 상속할 목적으로만 사용된다.


2. 프로젝트에 적용하기

BaseEntity

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime updatedDate;
}
  • @MappedSuperclass가 붙은 BaseEntity는 데이터베이스 테이블과 매핑되지 않고 자식 클래스 엔티티의 매핑 정보를 상속하기 위해 사용된다.

  • 이 클래스를 인스턴스화 해서 사용할 일이 없으므로 추상 클래스로 만드는 것이 좋다.

  • Spring Data JPA는 Auditing이라는 기능을 제공한다. 이를 통해 엔티티가 생성되고 변경되는 시점을 감지하여 자동으로 생성시각, 수정시각 등을 기록할 수 있다.

    • Auditing 기능을 사용하기 위해 다음과 같이 Application 클래스에 @EnableJpaAuditing 어노테이션을 붙여 Auditing을 활성화해야 한다.

      @SpringBootApplication
      @EnableJpaAuditing
      public class ShopApplication {
      
      	public static void main(String[] args) {
      		SpringApplication.run(ShopApplication.class, args);
      	}
      }
    • Auditing을 적용할 클래스에 @EntityListeners(AuditingEntityListener.class) 어노테이션을 붙여 준다. 그러면 해당 어노테이션이 엔티티의 변화를 감지하여 값을 기록해준다.

    • 생성일을 기록하기 위해 생성일을 나타내는 필드에 @CreateDate을 붙여주고, 수정일을 기록하기 위해 수정일을 나타내는 필드에 @LastModifiedDate을 붙여준다.


3. 임베디드 타입과 비교

여러 필드 값을 묶어서 별도의 영역에 정의한다는 점에서 @MappedSuperclass와 임베디드 타입의 용도가 비슷하다고 느껴진다. 그렇다면 둘의 차이는 무엇일까?

먼저 각각의 특징을 살펴보자.

3.1. @MappedSuperclass

  • extend를 사용하여 클래스 간 상속관계가 생기지만, 데이터베이스 측면에서는 상속관계 매핑이 아니다.

  • 엔티티가 아니며, 데이터베이스 테이블과 매핑되지 않는다. 단지 자식 클래스에 매핑 정보만 제공한다.

  • 직접 생성해서 사용할 일이 없으므로 추상 클래스로 정의하는 것이 좋다.

  • 테이블과 관계가 없고 단순히 여러 엔티티에서 공통으로 사용하는 매핑 정보를 모으는 역할이다. 따라서 주로 등록일, 수정일, 등록한 사람, 수정한 사람과 같은 전체 엔티티에서 공통으로 사용하는 필드를 모을 때 사용한다.

@MappedSuperclass
public abstract class BaseEntity {

    private LocalDateTime createdDate;
    private LocalDateTime updatedDate;
}
public class User extends BaseEntity {

	@Id @GeneratedValue
    private Long id;
    
    private String username;
}

3.2. 임베디드 타입

  • 임베디드 타입은 새로운 값 타입이다.

  • 주로 기본 값 타입들을 모아서 만들기 때문에 복합 값 타입이라고도 한다.

  • 임베디드 타입은 엔티티가 아니며, 단순히 여러 값들을 하나로 묶어 놓은 것이다.

@Embeddable
public class Timestamped {

    private LocalDateTime createdDate;
    private LocalDateTime updatedDate;
}
public class User {

	@Id @GeneratedValue
    private Long id;
    
    private String username;
    
    @Embedded
    private Timestamped timedtamped;
}

3.3. 비교

상속 VS 조합

상속을 하냐, 조합(위임)을 하냐의 차이이다. 객체지향 관점에서는 상속보다는 조합을 사용하는 것이 좋다. 그 이유는 다음과 같다.

  • 상속은 부모 클래스와 자식 클래스가 강한 결합성을 가져 캡슐화가 깨진다.

  • 부모 클래스의 변경사항이 자식 클래스에 영향을 준다. 예를 들어 부모 클래스의 메서드가 변경되면, 자식 클래스에서 해당 메서드의 실행이 오작동할 수 있다.

JPQL 작성시 편리함의 차이

그런데 임베디드 타입을 사용할 경우, JPQL 쿼리를 사용할 때 임베디드 타입을 적어주어야 한다.

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

반면 상속을 사용할 경우, 비교적 더 간단하게 작성할 수 있다.

select u from User u where u.createdDate = ?

결론

이번 경우, 임베디드 타입이 아닌 @MappedSuperclass를 사용했다. 그 이유는 다음과 같다.

  • 위에서 살펴본 상속의 단점을 생각해보면, 객체의 캡슐화가 깨지는 문제를 제외하고는 객체의 행위와 관련된 문제점이다. 예를 들어 부모 클래스의 메서드가 변경되었을 때 자식 클래스에서 메서드의 실행이 오작동하는 등의 문제이다. 따라서 등록일, 수정일과 같은 변수를 상속받는다고 그런 문제가 발생하지는 않을 것이다.
  • 그리고 BaseEntity의 등록일, 수정일과 같은 변수는 향후 크게 변경될 거 같지 않고, 변경되더라도 하위 클래스에 미치는 영향이 크지 않을 것이라 생각한다.

  • 또한 JPQL 사용시 @MappedSuperclass가 임베디드 타입보다 편리하다.

Refrerence

https://hudi.blog/spring-data-jpa-auditing-create-update-date/

https://velog.io/@rudwnd33/JPA-MappedSuperclass-vs-Embedded-Type

https://www.inflearn.com/questions/18578/%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-%ED%83%80%EC%9E%85%EA%B3%BC-mappedsuperclass-%EC%B0%A8%EC%9D%B4

https://lob-dev.tistory.com/entry/Base-Column%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-MappedSuperclass-%EC%99%80-embedded-Type-%EC%A4%91-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C

profile
공부한 것들을 기록합니다.
post-custom-banner

0개의 댓글