[JPA] 변경 사항 추적 기능 구현 - Envers
본문 바로가기

Web 개발/Java, SpringBoot, JPA

[JPA] 변경 사항 추적 기능 구현 - Envers

728x90
반응형

💡 사용 계기

JPA Auditing 기능을 이용하여 데이터의 추가, 수정을 관리하였습니다.

그러나 @CreatedBy 와 @LastModifiedBy 를 통해 처음과 마지막 수정에 대해서만 알 수 있고, 중간 수정에 대해서는 알 수 없었습니다.

그래서 JPA Envers 기능을 추가하게 되었습니다.

 

📖 개념 정리

1. Envers 

엔티티의 생성, 수정, 삭제 이력을 남길 수 있습니다.

Audit에 비해 모든 히스토리를 남기기 때문에 변경사항 추적할 수 있다는 점이 장점입니다.

하지만, 변경사항을 하나의 테이블에서 관리하기 때문에

추적해야하는 엔티티가 많은 경우 조회가 불편하다는 단점이 있습니다.

 

2. 한계

JPA가 자동으로 관리하기 때문에, 사용자 입력 데이터를 추가할 수 없습니다. (수정 사유 등)

 

💻 구현

1. 의존성 추가

dependencies {
    ...
    implementation 'org.springframework.data:spring-data-envers:2.7.2'
}

 

2. RevisionEntity 생성

package com.cauh.common.entity;

import com.sun.istack.NotNull;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.RevisionNumber;
import org.hibernate.envers.RevisionTimestamp;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Data
@RevisionEntity(CustomRevisionEntityListener.class)
@Entity
@Table(name = "c_revision_info")
@Slf4j
public class CustomRevisionEntity implements Serializable {
    private static final long serialVersionUID = ;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @RevisionNumber
    private long rev;

    @RevisionTimestamp
    @Column(nullable = false)
    private Long timestamp;

    @Column(name = "modified_by", columnDefinition = "nvarchar(4000)")
    private String modifiedBy;

    @NotNull
    public Date getRevisionDate() {
        return new Date(timestamp);
    }

    public String getModifiedBy() {
        return modifiedBy;
    }

    public void setModifiedBy(String modifiedBy) {
        this.modifiedBy = modifiedBy;
    }

}

 

3. CustomRevisionEntity에 유저 정보 저장

package com.cauh.common.entity;

import org.hibernate.envers.RevisionListener;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

public class CustomRevisionEntityListener implements RevisionListener {
    @Override
    public void newRevision(Object o) {
        CustomRevisionEntity customRevisionEntity = (CustomRevisionEntity) o;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = ((User)authentication.getPrincipal()).getUsername();
        customRevisionEntity.setModifiedBy(username);
    }
}

 

4. 추적하고자 하는 대상에, @Audit 추가

@Entity
@Audited
public class Board extends BaseEntity {
	...
}

 

5. 추적하고자 하는 대상의 레포지토리 변경

@Repository
public interface BoardRepository extends JpaRepository<Board, Long>, RevisionRepository<Board, Long, Integer> {
    ...
}

 

6. 변경사항 추적 활성화

@SpringBootApplication
@EnableJpaAuditing
@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
public class BoardApplication {

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

}

 

💬 실행 화면

 : 게시글 두개 작성 후, 첫번째 글을 수정하였습니다.

첫번째글을 "안녕하세요~~"에서 "안녕하세요~~!!!!"로 수정한 것을 알 수 있습니다.

 

 

 

 

728x90
반응형