Auditing(감사)
- 엔티티 변경 시점에 언제 , 누가 변경 했는지 정보를 기술하는 기능이다.
- 스프링 부트의 자동설정 지원을 받지 않는다.
누가, 언제
- @CreatedDate , @CreatedBy
- @LastModifiedDate , @LastModifiedBy
- 적용할 Entity 클래스에 @EntityListener 선언
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Comment {
@Id @GeneratedValue
private Long id;
private String title;
private String comment;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private int up;
private int down;
private boolean best;
// 언제 만들었는가
@CreatedDate
private Date created;
// 누가 만들었는가
@CreatedBy
@ManyToOne
private Account createdBy;
// 언제 수정했는가
@LastModifiedDate
private Date updated;
// 누가 수정했는가
@LastModifiedBy
@ManyToOne
private Account updatedBy;
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Account getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Account createdBy) {
this.createdBy = createdBy;
}
public Date getUpdated() {
return updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
public Account getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(Account updatedBy) {
this.updatedBy = updatedBy;
}
public int getUp() {
return up;
}
public void setUp(int up) {
this.up = up;
}
public int getDown() {
return down;
}
public void setDown(int down) {
this.down = down;
}
public boolean isBest() {
return best;
}
public void setBest(boolean best) {
this.best = best;
}
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", title='" + title + '\'' +
", comment='" + comment + '\'' +
", post=" + post +
", up=" + up +
", down=" + down +
", best=" + best +
", created=" + created +
", createdBy=" + createdBy +
", updated=" + updated +
", updatedBy=" + updatedBy +
'}';
}
}
사용
- @Configuration 클래스에 @EnableJpaAuditing 선언
- AuditorAware<Domain> 구현클래스 Bean 의 이름을 auditorAwareRef 속성에 기재한다.
- 원래는 SpringSecurity 의 SecurityContextHolder 를 사용해서 Authetnication 객체를 가져와 유저정보를 찾아내지만, 스프링 시큐리티 설정을 따로 안해놓아서 Entity 클래스를 사용했음.
@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "accountAuditAware")
public class SpringdatajpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringdatajpaApplication.class, args);
}
}
//AuditorAware<Domain> 구현클래스
@Service
public class AccountAuditAware implements AuditorAware<Account> {
@Autowired
private AccountRepository accountRepository;
@Override
public Optional<Account> getCurrentAuditor() {
Account account = new Account();
account.setUsername("Soomin");
account.setPassword("Jung");
account.setAge(28);
System.out.println("Looking for current user");
return Optional.of(accountRepository.save(account));
}
}
///
@RunWith(SpringRunner.class)
@SpringBootTest
public class AuditingTest {
@Autowired
private AccountRepository accountRepository;
@Autowired
private CommentRepository commentRepository;
@Autowired
private PostRepository postRepository;
@Test
@Transactional
public void testAuditing(){
Post post = new Post();
post.setTitle("JPA Auditing");
Post savedPost = postRepository.save(post);
Comment comment = new Comment();
comment.setTitle("is");
comment.setComment("Hibernate?");
comment.setUp(1);
comment.setBest(true);
comment.setPost(savedPost);
Comment savedComment = commentRepository.save(comment);
List<Comment> all = commentRepository.findAll();
all.forEach(System.out::println);
}
}
// 쿼리 진행 결과
Hibernate:
call next value for hibernate_sequence
Looking for current user
Hibernate:
call next value for hibernate_sequence
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
post
(created, title, id)
values
(?, ?, ?)
2020-11-19 20:24:19.007 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [TIMESTAMP] - [null]
2020-11-19 20:24:19.008 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [JPA Auditing]
2020-11-19 20:24:19.010 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [1]
Hibernate:
insert
into
account
(age, password, username, id)
values
(?, ?, ?, ?)
2020-11-19 20:24:19.014 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [28]
2020-11-19 20:24:19.014 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [Jung]
2020-11-19 20:24:19.015 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [Soomin]
2020-11-19 20:24:19.015 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [BIGINT] - [2]
Hibernate:
insert
into
comment
(best, comment, created, created_by_id, down, post_id, title, up, updated, updated_by_id, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-11-19 20:24:19.017 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BOOLEAN] - [true]
2020-11-19 20:24:19.017 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [Hibernate?]
2020-11-19 20:24:19.017 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [TIMESTAMP] - [Thu Nov 19 20:24:18 KST 2020]
2020-11-19 20:24:19.028 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [BIGINT] - [2]
2020-11-19 20:24:19.028 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [5] as [INTEGER] - [0]
2020-11-19 20:24:19.029 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [6] as [BIGINT] - [1]
2020-11-19 20:24:19.029 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [7] as [VARCHAR] - [is]
2020-11-19 20:24:19.029 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [8] as [INTEGER] - [1]
2020-11-19 20:24:19.029 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [9] as [TIMESTAMP] - [Thu Nov 19 20:24:18 KST 2020]
2020-11-19 20:24:19.030 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [10] as [BIGINT] - [2]
2020-11-19 20:24:19.030 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [11] as [BIGINT] - [3]
Hibernate:
select
comment0_.id as id1_1_,
comment0_.best as best2_1_,
comment0_.comment as comment3_1_,
comment0_.created as created4_1_,
comment0_.created_by_id as created_9_1_,
comment0_.down as down5_1_,
comment0_.post_id as post_id10_1_,
comment0_.title as title6_1_,
comment0_.up as up7_1_,
comment0_.updated as updated8_1_,
comment0_.updated_by_id as updated11_1_
from
comment comment0_
2020-11-19 20:24:19.045 TRACE 220 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_1_] : [BIGINT]) - [3]
Comment{id=3, title='is', comment='Hibernate?', post=Post{id=1, title='JPA Auditing', created=null}, up=1, down=0, best=true, created=Thu Nov 19 20:24:18 KST 2020, createdBy=Account{id=2, username='Soomin', password='Jung', age=28}, updated=Thu Nov 19 20:24:18 KST 2020, updatedBy=Account{id=2, username='Soomin', password='Jung', age=28}}
JPA LifeCycle Event 를 사용하는 방법도 있다.
엔티티에 변화가 일어날 시 Callback 을 실행할 수 있는 이벤트를 발생시킨다.
이를 받는 리스너를 등록하는 어노테이션들은 다음과 같다. 직관적 구성을 가지고 있다.
- @PrePersist
- @PreUpdate
- @PreRemove
- @PostPersist
- @PostUpdate
- @PostRemove
- @PostLoad
@Entity
public class Comment {
@Id @GeneratedValue
private Long id;
private String title;
private String comment;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private int up;
private int down;
private boolean best;
// 언제 만들었는가
@CreatedDate
private Date created;
// 누가 만들었는가
@CreatedBy
@ManyToOne
private Account createdBy;
// 언제 수정했는가
@LastModifiedDate
private Date updated;
// 누가 수정했는가
@LastModifiedBy
@ManyToOne
private Account updatedBy;
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Account getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Account createdBy) {
this.createdBy = createdBy;
}
public Date getUpdated() {
return updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
public Account getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(Account updatedBy) {
this.updatedBy = updatedBy;
}
public int getUp() {
return up;
}
public void setUp(int up) {
this.up = up;
}
public int getDown() {
return down;
}
public void setDown(int down) {
this.down = down;
}
public boolean isBest() {
return best;
}
public void setBest(boolean best) {
this.best = best;
}
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", title='" + title + '\'' +
", comment='" + comment + '\'' +
", post=" + post +
", up=" + up +
", down=" + down +
", best=" + best +
", created=" + created +
", createdBy=" + createdBy +
", updated=" + updated +
", updatedBy=" + updatedBy +
'}';
}
@PrePersist
public void prePersist(){
System.out.println("=======================");
System.out.println("Pre Persist is called.");
System.out.println("=======================");
}
}
///
@Test
@Transactional
public void testAuditing(){
Post post = new Post();
post.setTitle("JPA Auditing");
Post savedPost = postRepository.save(post);
Comment comment = new Comment();
comment.setTitle("is");
comment.setComment("Hibernate?");
comment.setUp(1);
comment.setBest(true);
comment.setPost(savedPost);
Comment savedComment = commentRepository.save(comment);
List<Comment> all = commentRepository.findAll();
all.forEach(System.out::println);
}
/// 영속화 시키기전에 Callback 이 실행되는 모습
Hibernate:
call next value for hibernate_sequence
=======================
Pre Persist is called.
=======================
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
post
(created, title, id)
values
(?, ?, ?)
2020-11-19 20:34:20.931 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [TIMESTAMP] - [null]
2020-11-19 20:34:20.932 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [JPA Auditing]
2020-11-19 20:34:20.933 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [1]
Hibernate:
insert
into
comment
(best, comment, created, created_by_id, down, post_id, title, up, updated, updated_by_id, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-11-19 20:34:20.938 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BOOLEAN] - [true]
2020-11-19 20:34:20.939 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [Hibernate?]
2020-11-19 20:34:20.939 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [TIMESTAMP] - [null]
2020-11-19 20:34:20.939 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [BIGINT] - [null]
2020-11-19 20:34:20.941 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [5] as [INTEGER] - [0]
2020-11-19 20:34:20.942 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [6] as [BIGINT] - [1]
2020-11-19 20:34:20.942 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [7] as [VARCHAR] - [is]
2020-11-19 20:34:20.942 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [8] as [INTEGER] - [1]
2020-11-19 20:34:20.943 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [9] as [TIMESTAMP] - [null]
2020-11-19 20:34:20.943 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [10] as [BIGINT] - [null]
2020-11-19 20:34:20.943 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [11] as [BIGINT] - [2]
Hibernate:
select
comment0_.id as id1_1_,
comment0_.best as best2_1_,
comment0_.comment as comment3_1_,
comment0_.created as created4_1_,
comment0_.created_by_id as created_9_1_,
comment0_.down as down5_1_,
comment0_.post_id as post_id10_1_,
comment0_.title as title6_1_,
comment0_.up as up7_1_,
comment0_.updated as updated8_1_,
comment0_.updated_by_id as updated11_1_
from
comment comment0_
2020-11-19 20:34:20.954 TRACE 3360 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_1_] : [BIGINT]) - [2]
'springframework > Spring Data JPA' 카테고리의 다른 글
@CreationTimeStamp, @UpateTimestamp (0) | 2020.12.24 |
---|---|
Spring Data JPA : Enumeration Mapping (0) | 2020.11.19 |
Spring Data JPA : Transaction (0) | 2020.11.19 |
Spring Data JPA : Query By Example (0) | 2020.11.19 |
Spring Data JPA : Specifications (0) | 2020.11.19 |