Auditing 직역은 감사하다. 감사를 수행하다.
이게 무슨 기능이냐면, 보통 테이블을 만들 때 등록일, 수정일 이 두개는 기본으로 깐다.
감사하다. 라는 뉘앙스 대로 뭔가 감찰하고, 뭐 그런건데,
저런 등록일이나 수정일, 더 나아가서는 등록자나 수정자까지
자동으로 넣어주는 기능이다.
등록자 수정자는 어떤 관리자가 수정을 했고 취소를 했는지 등 시스템 관리자 id? 등으로 넣어줌.
실무에서 많이 사용함.
그럼 우선 jpa로 등록일, 수정일 적용
먼저 보통 이런 것들은 모든 테이블에 공통적으로 들어가는 사항이라,
@Getter
@MappedSuperclass
public class JpaBaseEntity {
@Column(updatable = false)
private LocalDateTime createDate;
private LocalDateTime updatedDate;
@PrePersist
public void prePersist(){
LocalDateTime now = LocalDateTime.now();
createDate = now;
updatedDate = now;
}
@PreUpdate
public void preUpdate(){
updatedDate = LocalDateTime.now();
}
}
아예 이렇게 만들어 줌.
저 @MappedSuperclass가 뭐냐면,
superclass는 이제 부모 클래스를 보통 저렇게 말 하는데,
말 그대로 Mapped, 즉 얘가 다른 걸 가져와서 매핑시키는게 아니라 매핑 되어지는, 임.
https://qwefdg3.tistory.com/748
이거 보셈
기능 자체를 보자면 실체(테이블)가 아니라 속성을 상속받고 싶을 때
테이블 간의 상속관계는
https://qwefdg3.tistory.com/742
이거
둘이 다른거임. 하나는 속성만 상속받는, 즉 그냥 객체패러다임으로 엔티티의 특정부분을 공통화, 표준화 시킨거고,
아래 쪽은 진짜 DB세상에는 없는 테이블간의 상속관계에 대한 고민.
여튼 저걸 저렇게 @MappedSuperclass로 만들어서,
public class Member extends JpaBaseEntity{
@Id @GeneratedValue
@Column(name="member_id")
private Long id;
....
이런 식으로 상속시켜 버리면,
create table member (
age integer not null,
create_date timestamp(6),
member_id bigint not null,
team_id bigint,
updated_date timestamp(6),
username varchar(255),
primary key (member_id)
)
이렇게 테이블 만드는 쿼리가 나감.
그리고 당연히 객체간의 상속이니 member를 통해 부모 클래스 레벨의 데이터를 수정할 수 있음.
다음 볼거는,
@Getter
@MappedSuperclass
public class JpaBaseEntity {
@Column(updatable = false)
private LocalDateTime createDate;
private LocalDateTime updatedDate;
@PrePersist
public void prePersist(){
LocalDateTime now = LocalDateTime.now();
createDate = now;
updatedDate = now;
}
@PreUpdate
public void preUpdate(){
updatedDate = LocalDateTime.now();
}
}
여기 @prePersist와 @preUpdate인데,
이거는 JPA가 제공해 주는 것으로, 예상 했듯이 persist하기 전에, update하기 전에 하고 싶은 일을 지정시킬 수 있음.
DB에서 트리거와 같은 역할임.
보면 persist 하기 전에 createDate와 updateDate에 값을 넣고 persist.
update도.
updatable = false는 생성일은 수정될 이유가 없으니까.
@Test
public void JpaEventBaseEntity(){
Member member = new Member("member1", 10);
memberRepository.save(member);
em.flush();
em.clear();
Member findedMember = memberRepository.findById(member.getId()).get();
findedMember.setUsername("changeTime");
}
이걸 실제로 이렇게 테스트 코드 짜 보면,
(저장했다가, flush, clear해서 쿼리 완전히 보내고 영속성 컨텍스트 비워준 다음에, 가져와서 데이터 바꿔본거임.(더티체킹을 통해))
@Test
public void JpaEventBaseEntity(){
Member member = new Member("member1", 10);
memberRepository.save(member);
em.flush();
member.setUsername("changeTime");
}
사실 이렇게 해도 되는데 flush 이후로 시간 조금이라도 더 보내게 하고 싶어서
여튼 해 보면
생성 날짜와 변경 날짜. 자동으로 들어감.
jpa 주요 이벤트 어노테이션은 이제 저거 말고도,
내가 DB의 트리거와 같다고 비유 했듯이
@PrePersist, @PostPersist
@PreUpdate, @PostUpdate
Delete, Load(select) 등등 있음.
이번엔 SpringDataJpa에서 제공해주는 기능을 이용해 볼 것이며, 누가 생성, 수정했는지도 넣어 볼거임.
이거 사용 하려면, @EnableJpaAuditing이라는 걸, JpaAuditing 사용 가능하게 하겠다 라는 걸 메인 애플리케이션 띄우는 곳에다 명시해 줘야 함.
@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {
public static void main(String[] args) {
SpringApplication.run(DataJpaApplication.class, args);
}
}
이렇게.
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
@CreatedBy
@Column(updatable = false)
private String createBy;
@LastModifiedBy
private String lastModifiedBy;
}
저 EntityListeners가 이벤트 등록시켜버리는 거임. 저건 Jpa꺼임. 근데 AuditingEntityListener, 즉 감사 엔티티 리스너 저 이벤트 종류 자체는 스프링 꺼임.
@CreateDate 이제 저 종류가 스프링 거임.
보면 알듯이 생성일 용 column, 필드로 만드는 거임. 우리가 jpa에서 했던 것들이 이제 저 필드로 들어감.
@LastModifiedDate 도 최근 수정(update)일
@CreateBy
저게 누구에 의해서 생성되었느냐, 인데,
이것도 메인 메소드에 등록을 해 줘야 함.
@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {
public static void main(String[] args) {
SpringApplication.run(DataJpaApplication.class, args);
}
@Bean
public AuditorAware<String> auditorProvider(){
return ()-> Optional.of(UUID.randomUUID().toString());
}
}
아닌가 그냥 빈으로 등록시켜버리면 될 것 같기도 함.
보면 알겠지만 return 타입은 감사원 알아차리게 하는? 그런거고, 메소드명은 저거는 시스템 적으로 상관은 없긴 하지만 감사원 제공자.
여튼 저건 콜백으로 줘야 하는데, 지금은 그냥 UUID로 줬는데 실제로 할 땐 세션에서 가지고 오고 그래야 한다고 함.
public class Member extends BaseEntity{
@Id @GeneratedValue
@Column(name="member_id")
private Long id;
.......
상속받게 하고
@Test
public void JpaEventBaseEntity(){
Member member = new Member("member1", 10);
memberRepository.save(member);
em.flush();
em.clear();
Member findedMember = memberRepository.findById(member.getId()).get();
findedMember.setUsername("changeTime");
}
그냥 똑같은 거 실행
잘 됨.
저 @EntityListeners(~~) 하는 거 넣기 귀찮으면 xml로 뭐 하는 방법이 있긴한데, 붙일 부분이 많이 있지 않아서 xml 만드는 게 더 귀찮을 듯.
근데 보통,
등록일 수정일은 거의 필수로 들어가는데,
등록자 수정자는 필요 없는 경우가 있다.
그럴때는,
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
이렇게 테이블에 거의 필수로 들어가는 BaseTimeEntity를 만들고,
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity extends BaseTimeEntity {
@CreatedBy
@Column(updatable = false)
private String createBy;
@LastModifiedBy
private String lastModifiedBy;
}
생성자, 수정자 엔티티를 만들어 BaseTimeEntity를 상속받게 한다.
둘 다 JPA상에서 이벤트 리스너가 필요하니 달아주고,
둘 다 속성 상속을 다른 것쪽으로 따라붙게 할 거니 @MappedSuperclass
이제 생성일, 수정일만 필요한 엔티티는 BaseTimeEntity만 쓰면 되고, 생성자, 수정자도 필요한 건 저걸 쓰면 된다.
개인적으로 Entity라는 이름이 맘이 안들긴 한데. 뭔가 속성이라든지 그런 이름이 더 좋지 않았을까..
BaseProperty? BaseEntityAttribute?
'스프링데이터 + JPA > 스프링 데이터 JPA' 카테고리의 다른 글
23. 웹 확장 페이징과 정렬 (0) | 2023.11.24 |
---|---|
22. Web확장 도메인 클래스 컨버터 (0) | 2023.11.24 |
20. 확장기능. 사용자 정의 리포지토리 (0) | 2023.11.23 |
19. JPA 힌트, Lock (0) | 2023.11.23 |
18. @EntityGraph (0) | 2023.11.23 |