Envers - JPA Entity 자동이력관리
0. 개요
-
Envers
모듈은 하이버네이트 및 JPA와 함께 작동하는 핵심 모듈. -
엔티티 클래스를 위한 쉬운 감사 & 버전관리 솔루션 제공.
-
JPA 스팩에 정의 된 모든 매핑 감사
-
엔티티의 변경 이력을 자동 관리
-
트랜잭션 단위의 통합 Revision 관리(Snapshot)
-
1. 의존성
implementation 'org.springframework.data:spring-data-envers'
2. 부트 - Application.java
@EnableJpaAuditing
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
@EnableJapAuditing
: auditing 기능 활성화@EnableJpaAuditing(modifyOnCreate = false)
: 생성 시 수정일자를 함께 등록하지 않음. (default는 true, 함께 등록함)@EnableJpaAuditing(dateTimeProviderRef = "newYorkTime")
: 기준시간을 변경할 수 있음.
@EnableJpaAuditing(dateTimeProviderRef = "newYorkTime")
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
@Component
@NoArgsConstructor
public class newYorkTime implements DateTimeProvider {
@Override
public Optional<TemporalAccessor> getNow() {
return Optional.of(LocalDateTime.now().minusHours(14L));
}
}
DateTimeProvider
를 구현한 클래스를 빈으로 등록한 후, 오버라이드 한 메서드에 기준시간이 될 시간을 설정함.-
이후
@EnableJpaAuditing(dateTimeProviderRef = "___"
밑줄 부분에 구현한 Bean(클래스) 이름(Bean 이름이기 때문에 소문자로 시작)을 넣어주면 됨. @EnableJpaAuditing(auditorAwareRef = "___”
:@CreatedBy
나@ModifiedBy
를 사용할 때 유저 정보를 기록할 수 있음. (5-1에 추가)- 밑줄 부분에는 동일하게 구현한 Bean(클래스) 이름을 넣어주면 됨.(Bean 이름이기 때문에 소문자로 시작)
3. 엔티티 세팅 - Users.java
@Entity
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(value = {AuditingEntityListener.class})
public class Users {
@Id
Integer id;
String name;
@CreatedDate
private LocalDateTime regDt;
@LastModifiedDate
private LocalDateTime updateDt;
}
- 해당 3가지 세팅을 마치고 나면 하이버네이트 JPA를 사용한 엔티티 생성, 수정 시간이 타임스탬프처럼 자동으로 찍히게 됨
4. 리스너 환경설성 - AuditingEntityListener.java
-
기본세팅 말고 추가적인 설정을 원하면 Config로 설정하여 사용이 가능
@Configurable public class AuditingEntityListener { }
- AuditEntityListener는 Spring data jpa에서 구현한 EntityListener이다.
- 5번 추가기능의 콜백 메서드들은 여기서도 세팅이 가능하다.
5. 추가 기능
-
@CreateBy
,@LastModifiedBy
-
생성한 사람, 수정한 사람을 자동으로 저장하며, 해당 기능은 User에 대한 정보가 있어야 하기 때문에
AuditorAware
인터페이스를 구현한 클래스가 필요함@Component public class Auditor implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { return Optional.of("initial create"); } }
-
Spring Security
사용 중이라면Authentication
을 이용해서 가져올 수 있음public class SpringSecurityAuditor implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (null == authentication || !authentication.isAuthenticated()) { return Optional.empty(); } return Optional.of(authentication.getName()); } }
-
-
@PrePersist
,@PostPersist
,@PreUpdate
,@PostUpdate
-
@PrePersist
: 엔티티가 비영속 → 영속 상태가 되기 직전 호출 -
@PostPersist
: db에 insert된 후 호출 -
@PreUpdate
: 데이터를 업데이트하기 전에 호출 -
@PostUpdate
: 데이터를 업데이트 하고나서 호출 -
콜백 메서드로서 리턴타입은 항상 void로 해야함
-
Users.java
@Entity @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @AuditOverride(forClass = BaseEntity.class) public class Users extends BaseEntity { @Id Integer id; String name; String uuid; @PrePersist public void printPreTime() { UUID uuid1 = UUID.randomUUID(); this.uuid = String.valueOf(uuid1); System.out.println(uuid1); } @PostPersist public void printPostTime() { System.out.println(this.uuid); } @PreUpdate public void preUpdate() { System.out.println("업데이트 시작"); } @PostUpdate public void postUpdate() { System.out.println("업데이트 완료"); } }
[console] //add Hibernate: select users0_.id as id1_0_0_, users0_.reg_dt as reg_dt2_0_0_, users0_.update_dt as update_d3_0_0_, users0_.name as name4_0_0_, users0_.uuid as uuid5_0_0_ from users users0_ where users0_.id=? 20c0d5ed-6e62-4255-8516-da090fee2f99 Hibernate: insert into users (reg_dt, update_dt, name, uuid, id) values (?, ?, ?, ?, ?) 20c0d5ed-6e62-4255-8516-da090fee2f99 add id = 6, name: Myname //update Hibernate: select users0_.id as id1_0_0_, users0_.reg_dt as reg_dt2_0_0_, users0_.update_dt as update_d3_0_0_, users0_.name as name4_0_0_, users0_.uuid as uuid5_0_0_ from users users0_ where users0_.id=? 업데이트 시작 Hibernate: update users set reg_dt=?, update_dt=?, name=?, uuid=? where id=? 업데이트 완료 target id = 6, name: Myname-> string
-
6. 추가 사항
1. 추상클래스 활용
-
하나의 엔티티가 아니라 여러가지 엔티티에 동일한 이력관리를 적용하고 싶을때는 추상클래스를 활용하고 해당 클래스를 상속받아서 사용
-
BaseEntity.java
@Getter @Setter @MappedSuperclass @EntityListeners(value = {AuditingEntityListener.class}) public abstract class BaseEntity { @CreatedDate private LocalDateTime regDt; @LastModifiedDate private LocalDateTime updateDt; }
-
Users.java
@Entity @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @AuditOverride(forClass = BaseEntity.class) public class Users extends BaseEntity { @Id Integer id; String name; }
이렇게 사용하게 되면 기존 Users 클래스에서 이력관리를 위한 컬럼을 따로 설정해두지 않아도 되며, 테이블에는 BaseEntity의 변수들이 자동으로 컬럼으로 생성이 됨.
2. Querydsl 사용시
-
테스트를 하던 도중 해당 기능이 계속 제대로 동작하지 않아서 생각을 해보니, Querydsl을 사용하여 Update를 하고 있었던 것.
-
Querydsl은
JPARepository
를 구현하지 않는 방식으로 설계했기 때문에 해당 기능이 적용되지 않고 있었던 것. -
UserQuerydslRepository.java
@Repository @RequiredArgsConstructor public class UserQuerydslRepository { private final JPAQueryFactory queryFactory; QUsers user = new QUsers("U"); public void updateUser(Users updateUser) { queryFactory .update(user) .where(user.id.eq(updateUser.getId())) .set(user.name, updateUser.getName()) .set(user.updateDt, LocalDateTime.now()) .execute(); } }
→ 직접 이력을 관리해주는 방법
참고 레퍼런스
- https://leo0842.github.io/spring/date-auto-create/
- https://gist.github.com/dlxotn216/94c34a2debf848396cf82a7f21a32abe
- https://m.blog.naver.com/rorean/221485891788
마지막 수정일시: 2023-01-30 23:40
댓글남기기