데이터를 새로 추가하거나 수정했을 때
당시의 날짜와 누가 했는지도 같이 데이터에 저장하려면 어떻게 해야 할까?
Auditing
엔티티를 생성하거나 수정을 할 때
해당 시간과 수행한 사람을 찾기 위해서 사용한다.
(생성일, 수정일, 생성자, 수정자)
스프링 데이터 JPA에서 제공하는 Auditing
1. 처음 스프링 부트를 설정해주는 클래스에 @EnableJpaAuditing 어노테이션을 적용시켜준다.
@EnableJpaAuditing
@SpringBootApplication
public class ShoppingApplication {
public static void main(String[] arg) {
SpringApplication.run(ShoppingApplication.class, args);
}
}
@EnableJpaAuditing은
스프링 데이터 JPA에서 제공하는 Auditing 기능을 활성화하기 위한 어노테이션으로
해당 어노테이션을 사용해야 Auditing전용 클래스에서 사용하는 생성일, 수정일, 생성자, 수정자 등의 데이터를 자동으로 관리할 수 있다.
2. 따로 Auditing 전용 클래스를 만들어준 다음 각 기능을 제공해주는 어노테이션들을 추가해준다.
package study.datajpa.entity;
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class TimeAndBy {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
@EntityListeners는
JPA에서 엔티티의 생명 주기 이벤트를 수신하는 리스너를 지정하는 어노테이션이다.
해당 어노테이션을 사용하면 엔티티의 변화에 대한 콜백 메서드를 정의할 수 있다.
그래서 이를 주로 Auditing을 구현할 때 사용된다.
AuditingEntityListener는
스프링 데이터 JPA에서 제공하는 Auditing을 위한 기본 리스너 중 하나로
주로 생성일, 수정일을 자동으로 관리하기 위해 사용한다.
즉, @EntityListeners(AuditingEntityListener.class)는
해당 TimeAndBy라는 클래스에 대한 리스너로 AuditingEntityListener를 사용한다는 의미이고
이로 인해 @CreatedDate, @LastModifiedDate 등 어노테이션을 사용할 수 있게 제공해준다.
@MappedSuperclass는
JPA에서 사용하는 것으로 부모 클래스를 테이블로 매핑하지 않고, 자식 클래스에서 해당 부모 클래스의 매핑 정보를 상속받도록 지정하기 위해 사용한다.
부모 클래스에는 테이블이 생성되지 않고, 자식 클래스에서만 테이블을 생성한다.
간단하게
상속을 해줄 때 속성만 상속을 해주는 것으로
진짜 상속을 해주는 것이 아닌 자신의 속성만 상속을 해줘서 테이블에서 사용할 수 있게 해준다.
그래서 해당 클래스를 상속 받는 엔티티에서는 해당 클래스에 있는 속성(createdDate, lastModifiedDate 등)이 테이블에 추가된다.
해당 어노테이션은 주로
코드 재사용성 및 상속 관계에서 공통된 매핑 정보를 하나의 부모 클래스에서 정의하여 자식 클래스에 상속받아 사용하기 위해서 사용한다.
@CreatedDate는
엔티티가 생성될 때의 시간을 자동으로 저장해준다.
@LastModifiedDate는
엔티티가 수정될 때의 시간을 자동으로 저장해준다.
@CreatedBy는
엔티티를 생성한 사용자의 정보를 저장해준다.
@LastModifiedBy는
엔티티를 수정한 사용자의 정보를 저장해준다.
@Column(updatable = false)는
데이터가 업데이트 될 때 해당 필드의 값은 업데이트가 되지 못하게 한다.
데이터를 수정할 때 실수로 건들게 되어 수정이 되면 안되는 데이터로 이를 설정해주는 것인데
건들지 않는다는 확신이 있다면 해당 어노테이션은 생략해도 된다.
혹시 모를 일을 대비하기 위해 작성한 것이다.
3. Auditing으로 작성한 클래스를 해당 기능이 필요한 엔티티에 상속을 해준다.
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Review extends TimeAndBy {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "review_id")
private Long reviewId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
private String title;
private String content;
private int star;
}
그러면 해당 Review 엔티티의 테이블을 보면 TimeAndBy에 만든 생성일, 수정일, 생성자, 수정자 속성이 추가되고
해당 데이터들은 자동으로 값이 저장된다.
하지만 여기 까지만 하면 생성일과 수정일은 자동으로 데이터가 저장되지만
생성자와 수정자는 데이터가 저장되지 않는다.
4. 생성자, 수정자를 처리해주는 AuditorAware를 빈으로 등록해준다.
@EnableJpaAuditing
@SpringBootApplication
public class ShoppingApplication {
public static void main(String[] arg) {
SpringApplication.run(ShoppingApplication.class, args);
}
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.of(UUID.randomUUID().toString());
}
}
AuditorAware는
현재 사용자 또는 시스템에서 엔티티의 생성자와 수정자를 결정하는 인터페이스로
이를 스프링 빈으로 등록하여 정의해줘야 생성자와 수정자에 데이터를 넣을 수 있다.
AuditorAware<String>은
해당 인터페이스를 구현하고, 사용자 식별자를 String 타입으로 사용한다.
() -> Optional.of(UUID.randomUUID().toString())은
AuditorAware를 구현하는 것으로
현재 사용자의 식별자로 UUID를 생성한다.
여기서 UUID.randomUUID().toString() 는
새로운 UUID를 랜덤으로 생성하고 이를 문자열로 반환하여 사용자 식별자로 설정한다.
Optional.of()는
생성된 사용자 식별자를 Optional로 감싸서 반환한다.
왜냐하면 값이 존재하지 않을 수도 있기 때문에 그렇다.
지금은 쿠키, 세션 등의 사용자의 정보를 등록하여 사용하지 않았기에
임의로 랜덤 사용자 식별자를 생성해서 사용했다.
만약 사용자의 정보를 구분하는 것이 생기면 이를 추가로 작성하면 된다.
생성일, 수정일만 필요한 경우가 있다면?
생성일과 수정일은 거의 대부분이 필요하지만
생성자와 수정자의 데이터는 굳이 필요하지 않은 경우가 많다.
이럴때에는 둘을 분리해주고 상속으로 묶어주면 필요에 따라 골라 사용할 수 있다.
생성일, 수정일만 사용하는 Auditing클래스
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class TimeCheck {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
생성일, 수정일, 생성자, 수정자 모두 사용하는 Auditing클래스
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class TimeAndByCheck extends TimeCheck{
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
이렇게 하면 상황에 따라서 엔티티에 필요한 Auditing클래스를 상속받아서 사용할 수 있다.
만약 TimeCheck를 상속 받아서 결과를 보자.
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Review extends TimeCheck {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "review_id")
private Long reviewId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
private String title;
private String content;
private int star;
}
이렇게 엔티티에는 없는 CreatedDate와 LastModifiedDate 속성이 추가되었고
값도 넣은 적이 없는데 자동으로 들어가게 된다.
Auditing 클래스를 만들지 말고 바로 엔티티에 사용할 수 있을까?
클래스로 따로 만들어서 상속하여 사용방법 말고 바로 필드 작성하듯이도 가능하다.
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Review{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "review_id")
private Long reviewId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
private String title;
private String content;
private int star;
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
}
'Spring Boot' 카테고리의 다른 글
JWT를 이용하여 로그인 구현하기 (+ Refresh Token ) (0) | 2024.04.21 |
---|---|
Spring Security에 대해서 (0) | 2024.04.18 |
상태 코드 반환하기 (0) | 2024.01.18 |
application.yaml에 작성한 내용 설명 (0) | 2023.12.22 |
서버 정지할 때 Build cancelled while executing task 에러 해결하기 (0) | 2023.12.18 |