Spring) @EnableJpaAuditing 이란?
보통 엔티티를 만들 때 아래와 같이 만들 것이다.
public class User {
@Id
private Long id
@Column
private String name
@Column
private DateTime createdAt;
@Column
private DateTime updatedAt;
@Column
private String useYn;
}
여기서 createAt, updatedAt, useYn 은 기본적으로 공통으로 사용되는 필드들인데, 해당 필드를 매번 엔티티를 생성할때마다 작성하고, setter를 사용해서 넣어주는 것이 여간 귀찮은 일이 아닐 수 없다.
이 때 사용되는게 @EnableJpaAuditing 어노테이션으로 볼 수 있다.
@EnableJpaAuditing 이란?
해당 어노테이션은 Spring Data JPA 에서 제공하는 auditing 기능을 활성하는 어노테이션으로서, 감사 기능을 활성화 하게 되면, 엔티티를 생성하거나 수정할 때 자동으로 생성 및 수정 시각 및 생성자/수정자를 엔티티에 설정할 수 있게 된다.
사용방법
해당 어노테이션을 사용하는 방법은 다음과 같다.
1. 설정 클래스에 @EnableJpaAuditing 어노테이션을 추가한다.
package org.awesomeboro.awesome_bro;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@SpringBootApplication(scanBasePackages ={"org.awesomeboro.awesome_bro"})
@EnableJpaAuditing
public class AwesomeBroApplication {
public static void main(String[] args) {
SpringApplication.run(AwesomeBroApplication.class, args);
}
}
2. BaseEntity 추상 클래스를 생성한다.
이 때, 클래스명은 꼭 BaseEntity가 아닌 자신이 원하는 이름으로 지어도 상관 없다.
package org.awesomeboro.awesome_bro.common;
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
// useYn의 디폴트 값을 y로 설정해준다.
@Column(length = 1,nullable = false,columnDefinition = "CHAR(1) default 'y'")
private String useYn = "y";
@CreatedDate
@Column(nullable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(nullable = false)
private LocalDateTime updatedAt;
public void softDelete(){
this.useYn = "n";
}
}
여기서 @MappedSuperclass 어노테이션은 JPA 엔티티 클래스들이 해당 클래스(BaseEntity)를 상속할 경우, BaseEntity의 필드들도 컬럼으로 인식하게 도와주는 어노테이션이다.
@EntityListeners(AuditingEntityListener.class) 어노테이션은 JPA에게 해당 엔티티는 감사 기능을 사용하는 것을 알리는 어노테이션이다.
@CreatedDate 어노테이션은 엔티티가 저장될 때 시간이 자동 저장되는 어노테이션이다.
@LastModifiedDate 어노테이션은 조회한 엔티티의 값을 변경할 때 시간이 자동 저장되는 어노테이션이다.
3. 엔티티 클래스에서 BaseEntity 상속
BaseEntity를 만들었으면, 실제 엔티티 클래스로 상속하면 자동으로 감사기능이 적용된다.
package org.awesomeboro.awesome_bro.user;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
import lombok.*;
import org.awesomeboro.awesome_bro.article.Article;
import org.awesomeboro.awesome_bro.articleComment.ArticleComment;
import org.awesomeboro.awesome_bro.common.BaseEntity;
import org.awesomeboro.awesome_bro.dto.user.AbstractUserDto;
import org.awesomeboro.awesome_bro.dto.user.UserUpdateRequestDto;
import org.awesomeboro.awesome_bro.userAuthority.UserAuthority;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@ToString(exclude = {"userAuthorities", "articleComments", "articles"})
public class User extends BaseEntity {
@jakarta.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 50,nullable = false)
private String name;
@Column(unique = true,length = 50,nullable = false)
private String email;
@Column(length = 50,nullable = false)
private String nickname;
@Column(length = 128,nullable = true)
private String password;
@Column(length = 20,nullable = true)
private String phoneNumber;
@Column(length = 30,nullable = false)
private String loginType;
@Column(length = 200,nullable = true)
private String socialId;
@Basic(fetch = FetchType.LAZY)
@Column(length = 200)
private String profilePicture;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<UserAuthority> userAuthorities = new ArrayList<>();
public void updateUserInfo(UserUpdateRequestDto user) {
this.name = user.getName();
this.nickname = user.getNickname();
this.phoneNumber = user.getPhoneNumber();
this.profilePicture = user.getProfilePicture();
}
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<ArticleComment> articleComments = new ArrayList<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Article> articles = new ArrayList<>();
public void updateFromDto(AbstractUserDto dto) {
this.name = dto.getName();
this.email = dto.getEmail();
this.nickname = dto.getNickname();
this.password = dto.getPassword();
this.phoneNumber = dto.getPhoneNumber();
this.loginType = dto.getLoginType();
this.socialId = dto.getSocialId();
this.profilePicture = dto.getProfilePicture();
}
}