[JPA] 공통 컬럼 @MappedSuperClass를 이용하여 상속받기
- DB접근기술/JPA
- 2020. 8. 18. 23:27
안녕하세요.
오늘은 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에 의해 영속화되는 과정이 트랜잭션 내에서 반영이 되고 난 후에야 확인할 수 있다.