entity가 생성되고 수정된 날짜와 시간을 아는 것이 게시판 커뮤니티 앱이나, 커머스 앱등에서 필요합니다.
1. entities에 생성되고 수정된 날짜와 시간
2. 어느 user가 entity X를 생성,수정했는 지 username
JPA에서 자동으로 넣어주는 기능이 Auditing 기능입니다.
시간을 제공하는 interface에 대한 두 가지 구현이 있습니다.
1번째 구현은 현재 날짜와 시간을 반환하고, app에서 사용됩니다.
2번째 구현은 고정된 날짜와 시간을 반환하고, 통합 테스트에서 사용됩니다.
``
public interface DateTimeService {
ZonedDateTime getCurrentDateAndTime();
}
The CurrentTimeDateTimeService class implements the DateTimeService interface. Its getCurrentDateAndTime() method simply returns the current date and time.
The source code of the CurrentTimeDateTimeService looks as follows:
DateTimeService를 구현한 CurrentTimeDateTimeService를 구현합니다.
public class CurrentTimeDateTimeService implements DateTimeService {
@Override
public ZonedDateTime getCurrentDateAndTime() {
return ZonedDateTime.now();
}
}
``
현재 시간에 대한 정보를 갖는 ZonedDateTime 객체를 생성하여 반환합니다.
Spring Data JPA의 (감사)Auditing 인프라는 현재 날짜와 시간을 가져와야 할 때 DateTimeProvider 인터페이스를 사용합니다. 즉, DateTime Service를 Spring Data JPA와 통합하려면 해당 인터페이스를 구현해야 합니다. 다음 단계를 수행하여 이 작업을 수행할 수 있습니다:
@Override
public Calendar getNow() {
return new GregorianCalendar.from(dateTimeService.getCurrentDateAndTime());``먼저 애플리케이션을 실행할 때 사용되는 DateTimeService bean을 만들어야 합니다. 루트 응용 프로그램 컨텍스트 구성 클래스(또는 XML 구성 파일)에 이 빈을 선언해야 합니다. 왜냐하면 이 빈은 두 개 이상의 구성 요소에서 사용될 수 있기 때문입니다. 루트 응용 프로그램 컨텍스트 구성 클래스(또는 XML 구성 파일)는 이러한 빈의 자연스러운 위치라고 생각합니다.
다음 단계를 수행하여 이 빈을 만들 수 있습니다:
현재 TimeDateTimeService() 메서드를 만들고 새 CurrentTimeDateTimeService 개체를 반환하여 이 메서드를 구현합니다.
@Bean 를 method에 추가합니다.
@Profile 을 메서드에 추가하고 해당 값을 Profiles.APPLICATION 로 설정합니다. 이렇게 하면 응용 프로그램이 시작될 때만 이 빈이 생성됩니다.
``
@Configuration
@ComponentScan("net.petrikainulainen.springdata.jpa")
@Import({WebMvcContext.class, PersistenceContext.class})
public class ExampleApplicationContext {
@Profile(Profiles.APPLICATION)
@Bean
DateTimeService currentTimeDateTimeService() {
return new CurrentTimeDateTimeService();
}
}
``
예제 응용 프로그램의 지속성 계층을 구성하는 구성 클래스를 다음과 같이 변경하여 이 작업을 수행할 수 있습니다:
DateTimeProvider 개체를 반환하고 DateTimeService 개체를 메서드 매개 변수로 사용하는 DateTimeProvider() 메서드를 생성합니다.
새 AuditingAwareDateTimeProvider 개체를 생성하여 메서드를 구현합니다.
@Bean 주석을 사용하여 작성된 메서드에 주석을 추가합니다.
@EnableJpaAuditing 주석을 사용하여 구성 클래스에 주석을 추가하고 DateTimeProvider bean(dateTimeProvider)의 이름을 dataTimeProviderRef 특성의 값으로 설정합니다.
영속성컨텍스트 설정 클래스
``
@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")
@EnableJpaRepositories(basePackages = {
"net.petrikainulainen.springdata.jpa.todo"
})
@EnableTransactionManagement
class PersistenceContext {
@Bean
DateTimeProvider dateTimeProvider(DateTimeService dateTimeService) {
return new AuditingDateTimeProvider(dateTimeService);
}
}
``
엔티티 클래스(ToDo)를 다음과 같이 변경해야 합니다:
creationTime 필드 값이 엔티티가 처음 영속화될(지속될) 때 설정되는 것을 보장해야합니다.
수정의 값을 확인할 필요가 있다시간 필드는 엔티티가 처음 영속화(지속)될 때 설정되고 엔티티의 정보가 업데이트될 때 업데이트되는 것을 보장해야합니다.
다음 단계를 수행하여 이러한 변경을 수행할 수 있습니다:
``
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "todos")
final class Todo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "creation_time", nullable = false)
@Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
@CreatedDate
private ZonedDateTime creationTime;
@Column(name = "description", length = 500)
private String description;
@Column(name = "modification_time")
@Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
@LastModifiedDate
private ZonedDateTime modificationTime;
@Column(name = "title", nullable = false, length = 100)
private String title;
@Version
private long version;
}
``
이렇게 Auditing 기능을 구현하기 위해 관련 @를 붙이는 Entity가 여러 개 있다면,
@MappedSuperClass, @EntityListners, @CreatedDate 를 붙인 추상 클래스를 생성하여 상속시키는 방법도 좋은 아이디어입니다.
``
@EntityListeners(AuditingEntityListener.class)
@MappedSuperClass
public abstract class BaseEntity {
@Column(name = "creation_time", nullable = false)
@Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
@CreatedDate
private ZonedDateTime creationTime;
@Column(name = "modification_time")
@Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
@LastModifiedDate
private ZonedDateTime modificationTime;
}
``
JPA의 도움없이(@없이) Auditing기능을 구현하려면 entity lifecycle events를 더하는 callback methods를 생성해서 필드의 값을 set할 수 있습니다.
``
@MappedSuperClass
public abstract class BaseEntity {
@Column(name = "creation_time", nullable = false)
@Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
private ZonedDateTime creationTime;
@Column(name = "modification_time")
@Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
private ZonedDateTime modificationTime;
@PrePersist
public void prePersist() {
ZonedDateTime now = ZonedDateTime.now();
this.creationTime = now;
this.modificationTime = now;
}
@PreUpdate
public void preUpdate() {
this.modificationTime = ZonedDateTime.now();
}
}
``
entity lifecycle events를 더하는 callback method가 아닌 JPA의 도움을 받아 구현하는 이유가 2가지 있습니다.
tests를 작성할때 생성,수정 시간이 정확해야한다면, Data JPA를 쓰면 같은 date와 time을 항상 반환하는 DateTimeProvider를 사용할 수 있기 때문입니다.
생성자, 수정자 값을 entity에 저장하는 경우 Data JPA의 도움을 받아야 하는데,
생성시간, 수정시간도 같은 방법으로 구현하는 것이 적절하기 때문입니다.
해당 클래스 모든 필드는 transient언급 없으면 컬럼으로 간주
AuditingFields는 자동으로 초기화해주니
나머지들만 생성자에서 초기화하기 위해 파라미터로 넣어준다.
new 키워드 쓰지 않고 생성할 수 있도록 생성자 private으로하고
팩터리 메서드 of메서드 생성
도메인 Article을 생성할때 어떤 값이 필요한지 알려줌
컬렉션에서 사용하고자한다면 리스트로 만들어서 반환하거나
게시판화면구성 , 리스트에서 중복요소제거하거나 정렬할떄
비교할수있어야함 동일성 동등성 검사
equals hashcode를 만들어줌
@EqualsAndHashCode 롬복을 사용하면 모든 필드를 이용해서 동일성 동등성검사 한다.
그럴 필요없다. 엔티티가 두 개체가 동일한것은 유니크 아이디 id만 같은지 보면 가능
퍼포먼스 극대화 엔티티 성격을 반영
db에 연결된 entity라면 id가 같으면 같은 개체일 것이다.
id는 non-null이므로 체크해준다.
id가 non-null이므로 바로 equals를 실행한다. non-null체크 안하면 Object를 이용해서 검사한다. Object의 equals는 null인 경우에 대비해있다. a != null && a.equals(b)
non-null을 체크한것을 사용
영속화를 하지 않았을 때 id가 null일 수 있음 db에 insert하기 전인 엔티티는 id null
영속화 하지않은것은 동등성 검사를 탈락한다 false반환
slice테스트를 하고 @DataJpaTest의 지원을 받는다.
DataJpaTest는 모든 단위 테스트에 Transactional이 걸림
Transactional 기본값이 롤백이라서 entity 값을 변경해도 롤백될거라 업데이트 쿼리가 실행되지
않는다. flush()를 해줘야한다. 그러면 업데이트 쿼리가 발생한다.
근데 롤백되서 반영되지는 않을 것이다? 쿼리를 실행한다음에 롤백해?
롤백은 db쿼리 실행되기 전에 하는 것 아닌가?
Jpaconfig import안하면 오디팅 자동이 안 됨
테스트 생성자주입패턴가능