티스토리 뷰

Spring

데이터의 모든 변경이력 관리하기 with Hibernate

개발하고싶은개발자 2023. 12. 23. 17:05

데이터를 수정하던 중에 이 모든 변경사항을 관리하는 방법은 없을까? 라는 의문이 들었다.

그래서 Aop, EventListener 등 생각을 해보고 관련 정보를 찾아보던 중 Hibernate의 envers라는 모듈을 알게 됐다.

 

정확히는 김영한님의 envers 모듈 세션을 듣게 됐고 해당 내용을 간략히 정리하려고 한다.

 

 

 

방법은 간단하다. 의존성을 추가하고 이력관리를 하고 싶은 Entity 위에 @Audited 어노테이션을 추가하면 된다.

 

 

 

예제를 통해서 좀 더 자세히 알아보자.

Member 정보는 아래와 같다

create table member
(
    id         serial primary key,
    name       varchar
);
@Entity
@Builder
@Getter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String name;
}

 

 

Member Entity class 위에 @Audited 어노테이션을 추가하면 member_aud 라는 테이블이 생기고 해당 테이블에 member에 대한 변경 이력이 저장된다

 

 

1. member 데이터 생성

@Test
void create() {
    Member member = Member.builder()
       .name("홍길동")
       .build();

    memberRepository.save(member);
}

 

 

위처럼 member 데이터를 생성하면 member_aud에 아래와 같은 정보가 생성된다

 

rev는 조금 후에 알아보기로 하고 revtype은 데이터에 대한 타입을 가리킨다.

  • 0은 생성
  • 1은 수정
  • 2는 삭제 이다.

나머지는 변경 후의 member 테이블에 있는 컬럼의 정보이다.

우리가 member 데이터를 생성한 대로 각 컬럼의 값이 채워진 것을 볼 수 있고 생성했으므로 revtype의 값은 0이다

 

 

2. member 데이터 수정

@Test
@Transactional
@Rollback(value = false)
void modify() {

    Member member = memberRepository.findByName("홍길동").orElseThrow(() -> new RuntimeException("not found member"));

    member.setName("일길동");
}

 

수정을 한대로 각 컬럼의 값이 채워진 것을 볼 수 있고 revType의 값은 1이다

 

 

3. member 데이터 삭제

@Test
@Rollback(value = false)
void delete() {
    Member member = memberRepository.findByName("일길동").orElseThrow(() -> new RuntimeException("not found member"));

    memberRepository.delete(member);
}

 

 

삭제를 하면 해당 데이터가 삭제되므로 모든 컬럼의 값이 null이 된 것을 볼 수 있고 revtype의 값은 2이다.

(soft delete를 해도 모든 컬럼의 값은 null로 채워진다)

 

이제 rev의 값을 알기 위해서 member에 team 연관관계를 추가해 보겠다.

Team 정보는 아래와 같다

create table team
(
    id   serial primary key,
    name varchar
);
@Getter
@Entity
@Audited
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Setter
    private String name;
}

 

Member entity와 table도 아래와 같이 수정해 주자(ddl-auto=update 이므로 entity class만 수정해 주겠다)

@Getter
@Entity
@Builder
@Audited
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String name;

	@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
	private Team team;
	
	public void setName(String name) {
		this.name = name;
	}
}

 

 

이 상태로 하나의 트랜잭션에서 member와 team을 모두 데이터를 변경해 보자.

@Test
@Transactional
@Rollback(value = false)
void transaction() {
    Team team = Team.builder()
       .name("1팀")
       .build();

    Member member = Member.builder()
       .name("홍길동")
       .team(team)
       .build();

    memberRepository.save(member);
}

 

member_aud
team_aud

 

member_audteam_aud를 보면 rev 값이 같은 것을 볼 수 있다. 

envers 의존성을 추가하면서 테이블이 하나 새로 추가됐을 텐데 revinfo라는 테이블이다.

 

revinfo 테이블은 아래와 같이 생겼다.

revinfo

 

member_aud와 team_aud에 있는 rev값이 바로 여기 revinfo 테이블에 있는 rev값이다. 

이 값은 트랜잭션을 나타내는데 값이 같으면 같은 트랜잭션 내에서 작업이 실행됐다는 것을 보여준다.

즉 하나의 트랜잭션에서 어떤 테이블들이 수정됐는지를 알 수 있게 해주는 값이다.

 

 

 

고급 기능

@Audited 어노테이션에 withModifiedFlag = true 옵션을 주면은 각 _aud 테이블에 컬럼의 수정여부를 나타내는 컬럼명_mod 컬럼들이 추가로 생성되고 각 컬럼에 boolean으로 수정 여부를 나타낸다.

@Getter
@Entity
@Builder
@Audited(withModifiedFlag = true) //withModifiedFlag = true 추가
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    private Team team;

    public void setName(String name) {
       this.name = name;
    }
}

 

 

member entity를 위처럼 변경 후 이름을 변경하는 작업을 실행하면 aud 테이블에 아래와 같은 데이터가 추가된다

 

name_mod와 team_mod 컬럼이 추가됐는데 우리가 작업한 것은 name만 변경했으므로 name_mod만 true로 돼 있는 것을 볼 수 있다.

 

 

 

이러한 변경이력을 조회할 수 있는 기능도 제공하니까 관심이 있으면 알아보도록 하자.

추가로 spring에서 변경이력에 대한 조회를 편하게 할 수 있는 spring-data-envers도 있다

 

 

'Spring' 카테고리의 다른 글

[Security] hasIpAddress 사용하기  (0) 2024.03.09
윈도우 함수 사용하기 - (1/3)  (0) 2023.12.22
트랜잭션의 전파속성 중 Nested 사용해보기  (0) 2023.12.02
DispatcherServlet  (1) 2023.11.18
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30