보통 엔티티에는 해당 데이터의 생성시간과 수정시간을 포함한다.
언제 만들어졌고, 언제 수정되었는지 등등 나중에 유지보수에 중요한 정보이기도 하다. 그래서 매번 DB를 insert, update 할 때 날짜 데이터를 등록/수정하는 코드들이 들어간다.
아래의 내용은 스프링 부트와 AWS로 혼자 구현하는 웹 서비스
에서 읽은 내용이다.
//생성일 추가 코드 예제
public void savePosts(){
...
posts.setCreateDate(new LocalDate());
postsRepository.save(posts);
...
}
이런 단순한 코드가 모든 테이블, 서비스 메소드에 포함 된다고 생각하면 귀찮고 코드가 지저분해진다.
그래서 쓰는것이 JPA Auditing
이다.
Java8부터는 기존에 쓰던 Date, Calendar 클래스가 아닌 LocalDate와 LocalDateTime을 사용한다.
Date의 문제점을 고친 타입이기 때문에 Java8이상일 경우 무조건 써야 한다고 생각하면 되겠다.
Calendar 클래스는 월 값이 명시하는 static 변수이름에 해당하는 월 -1 이었다.
ex) Calendar.FEBRUARY 2월을 나타내는 이것은 값이 1이다.
BaseTimeEntity.java
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
//모든 Entity의 상위 클래스가 되어 Entity들의 createDate, modifiedDate를 자동으로 관리하는 역할을 하는 클래스
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime modifiedDate;
}
BaseTimeEntity클래스는 모든 Entity의 상위 클래스가 되어 Entity들의 createDate, modifiedDate를 자동으로 관리하는 역할을 한다.
어노테이션 | 설명 |
---|---|
@MappedSuperclass | JPA Entity 클래스들이 BaseTimeEntity를 상속할 경우 필드들도 칼럼으로 인식하도록 한다 |
@EntityListeners | BaseTimeEntity클래스에 Auditing 기능을 포함시킴 |
@CreatedDate | Entity가 생성되어 저장될때 시간이 자동 저장된다 |
@LastModifiedDate | 조회한Entity가 값을 변경할 때 시간이 자동 저장된다 |
Posts.java
@Getter
@NoArgsConstructor
@Entity // 실제 DB테이블과 매칭되는 클래스
// 기본값으로 클래스의 camelCase 이름을 스네이크케이스 테이블과 매칭시켜준다.
public class Posts extends BaseTimeEntity {
@Id // 해당 테이블의 PK임을 명시
@GeneratedValue(strategy = GenerationType.IDENTITY) //Identity 옵션을 넣어야 auto_increment 설정 가능
private Long id;
@Column(length = 500, nullable = false) // 해당클래스는 변수만 선언해도 column이 되지만
// 추가로 변경이 필요한 옵션이 있을때 사용한다. ex) varchar max_length 기본값은 255인데, 500으로 늘리고 싶을때
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
//여기처럼 타입을 TEXT로 변경하거나, null값을 못넣게 설정할 수 있다.
private String content;
private String author;
@Builder
public Posts(String title, String content, String author){
this.title = title;
this.content = content;
this.author = author;
}
public void update(String title, String content){
this.title = title;
this.content = content;
}
}
마지막으로 JPA Auditing 어노테이션을 모두 활성화 하기 위해서 Application 클래스에 활성화 어노테이션을 붙여준다.
Application.java
@EnableJpaAuditing //JPA Auditing 활성화
@SpringBootApplication
public class Application {
public static void main(String[] args){ SpringApplication.run(Application.class, args); }
}
이제 실제 코드는 완성 됐으니까 기능 동작을 위해 테스트를 수행한다.
@RunWith(SpringRunner.class)
@SpringBootTest
public class PostsRepositoryTest {
@Test
public void BaseTimeEntity_등록(){
//given
LocalDateTime now = LocalDateTime.of(2021, 5, 25, 0, 0, 0);
postsRepository.save(Posts.builder()
.title("title")
.content("content")
.author("author")
.build());
//when
List<Posts> postsList = postsRepository.findAll();
//then
Posts posts = postsList.get(0);
System.out.println(">>>>>>> createDate = " + posts.getCreatedDate() + ", modifiedDate = " + posts.getModifiedDate());
assertThat(posts.getCreatedDate()).isAfter(now);
assertThat(posts.getModifiedDate()).isAfter(now);
}
}
이로 인해 앞으로 등록/수정일로 고민할 필요는 없어졌다. 넣고자 하는 엔티티에 BaseTimeEntity만 상속받으면 알아서 해결해준다.
하지만 이 형식대로 하면 date format이 동작하지 않는데
@DateTimeFormat
, @JsonFormat
들도 Auditing에선 작동하지 않는다.
이럴때 찾은 방법은 아래와 같다.
@MappedSuperclass
@Getter
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
private String createdAt;
private String modifiedAt;
@PrePersist //해당 엔티티 저장하기 전
void onPrePersist(){
this.createdAt = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
this.modifiedAt = createdAt;
}
@PreUpdate //해당 엔티티 수정 하기 전
void onPreUpdate(){
this.modifiedAt = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
}
}
Pre, Post 에 따라 이전 이후 구분 가능
이랬던 format 형식을
이런식으로 변경하여 DB에 저장할 수 있다.
이상으로 JPA Auditing 에 대한 포스팅을 마친다.