여러 테이블에서 공통적으로 사용하는 필드중에는 생성일, 수정일 같이 날짜 필드들이 있다.
이렇게 여러 테이블에서 동일한 의미로 사용되는 필드들은 매번 엔티티를 만들어줄 때마다 적어주는 것은 귀찮은 일이다.
그래서 날짜 필드들만 추상화 시키기로 하였다.
아래는 기존 엔티티 클래스이다.
@Setter
@Getter
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Board{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 50)
private String title;
@Column(length = 255)
private String content;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
}
게시판 테이블에서는 엔티티가 하나이기 때문에 하나의 테이블에서 사용되지만,
일반적으로 createdAt, updatedAt, deletedAt과 같은 컬럼은 여러 테이블에서 공통적으로 사용되는 컬럼이다.
해당 컬럼이 사용되는 시점 또한 모두 동일하다.
createdAt은 생성 시, updatedAt은 생성과 수정 시(즉, set 메서드가 사용되는 모든 경우라 할 수 있음), deletedAt은 삭제 시 사용된다.
그래서 매번 엔티티마다 이렇게 똑같은 용도의 컬럼들을 넣어주는 것은 좋지 않은 방법이기에 추상화할 수 있는 방법을 알아보았다.
다행히 JPA에서는 이러한 문제점을 해결해줄 수 있는 어노테이션을 제공한다.
먼저 추상화 시킬 컬럼들을 담을 추상 클래스를 만들었다.
public abstract class DateEntity {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
}
추상 클래스가 갖고 있는 날짜 필드들을 엔티티 클래스에서 받아 날짜 필드 값들을 사용할 것이다.
그걸 가능하게 해주는 어노테이션은 @MappedSuperclass 이다.
@MapperSuperclass 어노테이션은 상속받은 엔티티들이 공통으로 사용할 수 있는 매핑 정보를 정의할 때 사용한다. @MapperSuperclass가 붙은 클래스 자체는 테이블로 매핑되지 않고, 상속받은 하위 클래서의 테이블에 해당 필드들이 매핑된다.
특징을 정리하면 다음과 같다.
그래서 아래와 같이 추상 클래스에 추가해줬다.
@MapperSuperclass
public abstract class DateEntity {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
}
이제 기존 엔티티 클래스는 날짜 필드는 DateEntity 클래스로부터 상속받아 사용하니 필요없게 되었다.
그렇다면 이제 createdAt, updatedAt, deletedAt 필드에는 값을 어떻게 넣고 수정할 수 있을까?
기존에는 생성자로 받게 해놨었는데 이제는 Board 클래스가 날짜 필드들을 갖고 있지 않으니 당연히 에러가 나고 있다.
어떤 방법들이 있으지 찾아보니 JPA가 또 쉽게 해결할 수 있는 어노테이션을 제공해 준다고 한다.
엔티티가 생성되는 날짜를 필드의 값으로 선언한다.
즉, 해당 어노테이션을 필드에 달아주면 엔티티가 생성될 때의 날짜값을 넣어준다.
엔티티가 최근에 수정된 날짜를 필드의 값으로 선언한다.
해당 어노테이션이 달린 필드를 갖고 있는 엔티티가 마지막으로 변경된 날짜를 넣어준다.
deletedAt은 삭제해주는 어노테이션이 없기 때문에 값을 넣어줄 수 있는 메서드를 별도로 만들어줬다.
@Setter
@Getter
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Board extends DateEntity{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 50)
private String title;
@Column(length = 255)
private String content;
/* deleteAt 값 넣어주는 함수 추가 */
public void delete() {
this.setDeletedAt(LocalDateTime.now());
}
}
이제 필드값에 어떤한 값들이 들어가야하는지 설정했다면 자동으로 설정된 값이 등록되게 해줘야한다.
이때에는 @EnableJpaAuditing 어노테이션을 사용해주면 된다.
JPA에서 특정 엔티티에 대해 변경 사항을 자동으로 기록하도록 설정해준다.
Application 클래스에 @EnableJpaAuditing 어노테이션을 추가하였다.
@EnableJpaAuditing
@SpringBootApplication
public class BoardApplication {
public static void main(String[] args) {
SpringApplication.run(BoardApplication.class, args);
}
}
그리고 Jpa 이벤트가 발생할 때 콜백을 처리할 수 있는 @EntityListeners 어노테이션을 엔티티 클래스에 달아주었다.
엔티티에 어떠한 이벤트가 발생할 때 이전, 이후 처리하고싶은 함수가 있는 등의 상황에서 해당 어노테이션을 달아주고 지원해주는 콜백 메서드를 사용해주면 간편하게 활용할 수 있다.
이번 경우에는 해당 엔티티가 Auditing 기능이 처리가 될 것이기 때문에 이 이벤트에 대한 설정만 달아주면 된다.
이런 경우에는 AuditingEntityListener.class 를 추가해주면 된다.
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class DateEntity {
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@Setter
private LocalDateTime deletedAt;
}
추상 클래스에 추가하여 추상 클래스를 상속받는 모든 엔티티 클래스에 적용될 수 있도록 하였다.
참고
https://docs.spring.io/spring-data/jpa/reference/auditing.html
https://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/listeners.html#d0e3053