만족은 하되 안주하지는 말자

기록해야 기억한다

프로그래밍/backend&devOps

[SpringBoot] Entity 생성시간, 수정시간 자동화 하기 (JPA Entity 에 CreatedDate, ModifiedDate Auditing 하기)

D36choi 2020. 9. 2. 14:40
728x90

문제

게시판의 게시물등의 Entity 를 만들때 생성일시와 마지막으로 수정한 일시를 추가하고 싶다.

Entity 정의

채용플랫폼의 지원서 테이블을 만들어보고자 한다.

@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Application {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Long applicationId;

//    @Column(nullable = false)
//    private Date dateCreated;
//      이렇게 꼭 멤버 선언을 해줘야 할까?
//    @Column(nullable = false)
//    private Date lastUpdated;

    @Column(nullable = false)
    private Integer bit;

    @Column(nullable = false)
    private String question1;

    @Column(nullable = false)
    private String question2;

    @Column(nullable = false)
    private String question3;

    @Column(nullable = false)
    private String educationLevel;

    @Column(nullable = false)
    private String militaryService;

원래는 주석처리한 저 두개를 선언해서 관리해야했는데, 그러지 않아도 된다고 한다

EnableJpaAuditing 어노테이션을 이용할 수 있도록 하자!

BaseTimeEntity 추상클래스를 만든다.

package com.hanium.hfrecruit.domain;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

/**
 * '@MappedSuperclass' : JPA Entity 클래스들이 BaseTimeEntity를 상속할 경우 필드들(createdDate, modifiedDate)도 칼럼으로 인식하도록 합니다.
 * '@EntityListeners(AuditingEntityListener.class)': BaseTimeEntiy 클래스에 Auditing 기능을 포함시킵니다.
 * '@CreatedDate': Entity가 생성되어 저장될 때 시간이 자동 저장됩니다.
 * '@LastModifiedDate': 조회한 Entity의 값을 변경할 때 시간이 자동 저장됩니다.

 */
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;

    public LocalDateTime getCreatedDate() {
        return createdDate;
    }

    public LocalDateTime getModifiedDate() {
        return modifiedDate;
    }
}

velog.io/@conatuseus/2019-12-06-2212-%EC%9E%91%EC%84%B1%EB%90%A8-1sk3u75zo9

LastModifiedDate 어노테이션이 붙으면 엔티티 값 수정시 알아서 변경을 시켜준다.

내가 일일이 기록을 하는 방법을 구현할 필요가 없는것...!

Entitiy 클래스에 상속이 되도록 하자

public class Application extends BaseTimeEntity {
...

}

그리고 위 주석처리된 부분을 지워보도록 하자

테스트 코드를 작성해 단위 테스트를 해보자

package com.hanium.hfrecruit.repository;

import com.hanium.hfrecruit.domain.application.Application;
import com.hanium.hfrecruit.domain.application.ApplicationRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;
import java.util.List;

@SpringBootTest
public class ApplicationTest {

    @Autowired
    ApplicationRepository applicationRepository;

    @Test
    public void BaseTimeEntity_등록() {
        //given
        LocalDateTime now = LocalDateTime.of(2019, 12, 6, 0, 0, 0);

        applicationRepository.save(new Application());

        //when
        List<Application> applications = applicationRepository.findAll();

        //then
        Application app = applications.get(0);

        System.out.println(">>>>> createDate = "+app.getCreatedDate() + ", modifiedDate = "+app.getModifiedDate());

//        assertThat(application.getCreatedDate()).isAf;
//        assertThat(application.getModifiedDate()).isAfter(now);
    }
}

하지만 에러가 발생한다. 왜일까?

org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.hanium.hfrecruit.domain.application.Application.bit; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : com.hanium.hfrecruit.domain.application.Application.bit

Non Null 인 프로퍼티에 대해 적절하게 처리를 안했기 때문. 단순히 new Application() 으로는 생성할 수 없는 듯 하다.

 

빌더를 통해 entity save 테스트를 해보자

Non Null 인 멤버에 대해 단순히 객체 생성으로 테스트를 하면 에러가 난다. 그러니 빌더를 통해 적절히 객체를 생성할 수 있도록 하자!

 

@Builder 어노테이션 사용을 위해서는 Lombok 라이브러리가 필요하다.

https://projectlombok.org/features/Builder

 

@Builder

 

projectlombok.org

빌더 어노테이션을 통해 모델의 객체 생성을 직관적으로 할 수 있다.

@Builder
    public Application(Integer bit,String question1,String question2,
                       String question3,String educationLevel, String militaryService)
    {
        this.bit = bit;
        this.question1 = question1;
        this.question2 = question2;
        this.question3 = question3;
        this.educationLevel = educationLevel;
        this.militaryService = militaryService;
    }
}

Entity 클래스의 최하단에 위와 같은 함수를 만들어주자!

필요한 parameter 들은 선택해서 만들 수 있다. 

package com.hanium.hfrecruit.repository;

import com.hanium.hfrecruit.domain.application.Application;
import com.hanium.hfrecruit.domain.application.ApplicationRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;
import java.util.List;

@SpringBootTest
public class ApplicationTest {

    @Autowired
    ApplicationRepository applicationRepository;

    @Test
    public void BaseTimeEntity_등록() {
        //given
        LocalDateTime now = LocalDateTime.of(2019, 12, 6, 0, 0, 0);
        Application application = Application.builder().bit(0)
                .question1("이 회사에 지원한 동기는?")
                .question2("대학교 생활 중 가장 뜻깊었던 성취는?")
                .question3("우리 회사에서 어떤 사람으로 성장하고 싶은지?")
                .educationLevel("석사졸")
                .militaryService("육군 만기 전역")
                .build();

        applicationRepository.save(application);

        //when
        List<Application> applications = applicationRepository.findAll();

        //then
        Application app = applications.get(0);

        System.out.println(">>>>> createDate = "+app.getCreatedDate() + ", modifiedDate = "+app.getModifiedDate());

//        assertThat(application.getCreatedDate()).isAf;
//        assertThat(application.getModifiedDate()).isAfter(now);
    }
}

new Application() 대신 @Builder 를 통해 만든 객체를 담아준다.

마지막으로 Main 함수에 @EnableJpaAuditing 어노테이션을 붙여준다

@EnableJpaAuditing
@SpringBootApplication
public class HfrecruitApplication {

	public static void main(String[] args) {
		SpringApplication.run(HfrecruitApplication.class, args);
	}

}

 

 

자 이제 Test를 돌려보자.

성공