[JPA] 공통 컬럼 @MappedSuperClass를 이용하여 상속받기

안녕하세요.

 

오늘은 JPA를 이용하여 @Entity 클래스를 설계하는 도중 공통 속성인 created_at(insert time), updated_at(update time) 컬럼에 대하여 모든 @Entity 클래스에서 공통적으로 소유할 가능성이 보여 따로 공통 엔티티 클래스인 BaseEntity 클래스로 분리하는 방법에 대해 소개하겠습니다.

 

먼저 기존 코드입니다.

 

@AllArgsConstructor
@Getter
@ToString
@EqualsAndHashCode(of = "user_seq")
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long user_seq;
    private LocalDateTime created_at;
    private LocalDateTime updated_at;
    private String name;
    private Integer age;

    public User(AddUserParam param) {
        this.name = param.getName();
        this.age = param.getAge();
        LocalDateTime now = LocalDateTime.now();
        this.created_at = now;
        this.updated_at = now;
    }

    protected User() {}
}

다음과 같이 엔티티 내에 직접적으로 created_at과 updated_at 같은 timestamp 컬럼을 추가합니다.

 

이는 순수 JPA를 사용할 때와 Spring Data JPA를 사용할 때 다른 모습으로 공통 엔티티를 추출하여 처리할 수 있습니다.

 

순수 JPA를 이용한 BaseEntity 클래스 설계

@Setter
@Getter
@MappedSuperclass
public class BaseEntity {
    @Column(updatable = false)
    private LocalDateTime created_at;
    private LocalDateTime updated_at;

    @PrePersist
    public void prePersist() {
        LocalDateTime now = LocalDateTime.now();
        created_at = now;
        updated_at = now;
    }

    @PreUpdate
    public void preUpdate() {
        updated_at = LocalDateTime.now();
    }
}

@Column(updatable = false) : created_at 컬럼은 insert 시에 들어가는 값이 쭉 유지되어야 하므로 update 되어서는 안되기에 처리

 

순수 jpa는 EntityManger를 이용한 persist() 메소드를 통해 엔티티를 영속화시키는 작업이므로 persist 메소드가 실행되기 전 BaseEntity 객체를 상속하는 @Entity 클래스의 created_at과 updated_at 값이 현재시각으로 채워지는 prePersist() 메소드가 실행됩니다.

 

또한 변경감지(dirty checking) 등을 이용하여 update 쿼리가 실행되었을 때, updated_at을 현재시각으로 수정하는 preUpdate() 메소가 실행됩니다.

 

이들은 모두 이벤트성 메소드로 스프링 데이터 JPA를 이용했을 시에는 특정한 이벤트 클래스에 의해서 관리됩니다.

(AuditingEntityListener)

 

 

스프링 Data JPA를 이용한 BaseEntity 클래스 설계

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

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

    @LastModifiedDate
    private LocalDateTime updated_at;
}

좀더 간단한 모습으로 구현되었으나 AuditingEntityListener 클래스타입을 @EntityListeners 의 인자로 전달해야 위의 @PrePersist 어노테이션과 @PreUpdate 어노테이션의 기능처럼 @CreatedDate, @LastModifiedDate 어노테이션을 이용하여 객체를 insert 할때, update 할때 now 값을 넣어주게 됩니다.

 

최종적으로 User 엔티티 클래스는 다음과 같은 모습입니다.

 

@AllArgsConstructor
@Getter
@ToString
@EqualsAndHashCode(of = "user_seq")
@Entity
@Table(name = "users")
public class User extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long user_seq;
    private String name;
    private Integer age;

    public User(AddUserParam param) {
        this.name = param.getName();
        this.age = param.getAge();
        LocalDateTime now = LocalDateTime.now();
        this.created_at = now;
        this.updated_at = now;
    }

    protected User() {}
}

무엇이든지 상속만 받으면 된다고 생각하겠지만!!

 

EntityManager에 의해 영속화되는 과정이 트랜잭션 내에서 반영이 되고 난 후에야 확인할 수 있다.

댓글

Designed by JB FACTORY