Closed binchoo closed 3 years ago
이해하기 쉬운 예제 추가 필요)
Envers는 엔터티 자료에 대한 버전 관리를 제공한다.
엔터티 클래스 혹은 필드에 @Audited
를 명시하면, 하이버네이트는 각 엔터티에 대응하는 이력 테이블을 생성한다.
감사 대상인 엔터티에 변경이 가해질 때마다 이력이 업데이트 된다. 이력 정보를 저장하고 읽어들이는 전략에는 2가지가 있다.
감사 대상 테이블에 row가 삽입/갱신/삭제될 때마다 이력 테이블에도 row가 삽입되며 해당 row가 유효했던 revision 번호도 함께 저장한다. 이력 테이블에 추가된 row들은 결코 수정될 일이 없다.
List<Book> books = reader.createQuery()
.forEntitiesAtRevision(Book.class, 3).getResultList();
SELECT
book_aud0_.id AS id1_3_,
book_aud0_.rev AS rev2_3_,
book_aud0_.revtype AS revtype3_3_,
book_aud0_.isbn AS isbn4_3_,
book_aud0_.title AS title5_3_,
book_aud0_.author_id AS author_i6_3_
FROM book_audit book_aud0_
WHERE book_aud0_.rev =
(
SELECT MAX(book_aud1_.rev)
FROM book_audit book_aud1_
WHERE book_aud1_.rev <= ?
AND book_aud0_.id = book_aud1_.id
)
AND book_aud0_.revtype <> ?
감사 대상 테이블에 row가 삽입/갱신/삭제될 때마다 이력 테이블에도 row가 삽입되며 해당 row가 유효했던 start-revision 번호도 저장한다. 그리고 이미 존재하는 이력 rows의 end-revision 필드에 방금 전 start-revision와 똑같게 수정한다. 따라서 특정 버전을 조회하는 쿼리를 'start-revision ~ end-revison' 범위를 명시하는 형태를 사용할 수 있게 된다.
자바는 위랑 동일
SELECT
book_aud0_.id AS id1_3_,
book_aud0_.rev AS rev2_3_,
book_aud0_.revtype AS revtype3_3_,
book_aud0_.revend AS revend4_3_,
book_aud0_.isbn AS isbn5_3_,
book_aud0_.title AS title6_3_,
book_aud0_.author_id AS author_i7_3_
FROM book_audit book_aud0_
WHERE book_aud0_.rev <= ?
AND book_aud0_.revtype <> ?
AND (book_aud0_.revend > ?
OR book_aud0_.revend IS NULL)
SELECT 서브 쿼리 없이 더 효율적인 쿼리가 작성된다.
@Query
@Query
애너테이션을 붙임으로써 커스텀 쿼리 사용 가능
엔티티 클래스의
클래스 시그니처 위에 @EntityListeners(AuditingEntityListener.class)
를 붙이고
다음의 애너테이션이 붙여진 4개의 필드를 추가
@CreatedDate
@CreatedBy
@LastModifiedBy
@LastModifiedDate
@MappedSuperclass
를 이용한 공통 감사 사항 적용@EntityListeners(AuditingEntityListener.class)
를 붙여진 추상 클래스를 공통 부모로 사용@EnableJpaAuditing(auditorAwareRef="auditorAwareBean")
를 붙이고https://github.com/caffeine-library/pro-spring-5/issues/64#issuecomment-939415148 주제가 직관적으로 와닿지 않아 실제 이력 테이블 모습을 살펴본 것입니다.
1. 김이름 추가
2. 이이름 추가
3. 박이름 추가
4. 김이름->김삼름 변경
5. 이이름->이삼름 변경
6. 박이름->박삼름 변경
위 시나리오를 수행하면 SINGER_AUDIT
테이블은 그림처럼 됩니다.
이제 엔버스의 감사 전략에 따른 SINGER_AUDIT_H
(이력 테이블) 모습을 살펴 봅니다.
이력 테이블을 보면, 시나리오에서 수행한 각 조작이 낳은 엔터티 결과물에
AUDIT_REVSION
값이 붙어서 기록된 걸 볼 수 있습니다.
(붉은 부분은 기본 감사 전략에선 쓰지 않는 리던던트한 칼럼입니다.)
유효성 감사 전략에서는 AUDIT_REVISION
뿐만 아니라 AUDIT_REVISION_END
값도 기록됩니다.
AUDIT_REVISION_END
값이 null이 아니라면 엔터티가 해당 리비전부터 무효가 되었음을 나타냅니다.
예를 들어 4번 조작에서 '김이름'이 '김삼름'이 되었으므로 '김이름'은 1~3 리비전동안 유효하고 4 리비전부터 무효입니다.
AUDIT_REVISION
, AUDIT_REVISION_END
는 글로벌로 관리되는 값이다AUDIT_REVISION
~ AUDIT_REVISION_END - 1
리비전동안 유효한 엔터티였다.배치 수정에 대해서도 한 번 살펴보겠습니다. (유효성 감사 전략만)
1. 김이름 추가
2. 이이름 추가
3. 박이름 추가
4. 한 번에 수정
김이름->김삼름 변경
이이름->이삼름 변경
박이름->박삼름 변경
시나리오의 4번째 조작은 한 번에 값들을 수정하는 쿼리를 날리는 것입니다.
따라서 이력 테이블에 3개의 row가 추가되었고 리비전은 4가 동일하게 붙습니다.
기존의 이력의 AUDIT_REVISION_END
에 4를 붙여, 리비전 4부터 이들이 무효임을 기록해줍니다.
배치 삭제에 대해서도 한 번 살펴보겠습니다. (유효성 감사 전략만)
1. 김이름 추가
2. 이이름 추가
3. 박이름 추가
4. 모두 삭제
엔터티가 삭제될 것이기 때문에 이력 테이블에 NOT NULL
조건을 없애줍니다.
파란색 부분을 살펴봅시다. 엔터티 삭제가 발생하면 이력 테이블은 해당 ID 값은 유지하되 이외 값들은 null로 날려버립니다.
SELECT * FROM SINGER_AUDIT_H WHERE ID=1 ORDER BY AUDIT_REVISION;
1. 김이름 추가
2. 이이름 추가
3. 박이름 추가
4. 배치 수정 쿼리
박이름->박삼름 변경
김이름, 이이름 변경 없이
[수행 후 이력 테이블]
배치 수정 쿼리에 수정 없는 2개의 엔터티가 포함되었는데요 이러면 수정이 일어난 '박이름'->'박삼름' 엔터티만 이력이 추가됩니다~
주제
8장 스프링에서 JPA2로 데이터 액세스하기 (2)를 읽고 중요✨ 하다고 생각하는 키워드와 선택한 이유에 대해서 코멘트로 달아주세요.
연관챕터
63