실제 서비스를 운영할 때는 보통 등로시간과 수정시간,등록자,수정자 를 테이블에 넣어 놓고 활용을 합니다.
그리고 데이터가 생성되거나 수정될 때 시간을 기록해주고, 어떤 사용자가 등록을 했는지 아이디를 남깁니다.
이 컬럼들은 버그가 있거나 문의가 들어왔을 때 활용이 가능합니다. 데이터를 대용량으로 데이터를 업데이트했는데,
다시 업데이트를 해야 할 경우 변경딘 대상을 찾을 때 활용할 수도 있습니다.
Spring Data Jpa에서는 Auditing 기능을 제공하여 엔티티가 저장 또는 수정될 때 자동으로 등록일,수정일,등록자,수정자를 입력해줍니다. Audit의 사전적 정의는 '감사하다' 입니다. 즉 , 엔티티의 생명성과 수정을 감시하고 있는 것입니다. 이런 공통 맴버 변수들을 추상클래스로 마들고, 해당 추상 클래스를 상속받는 형태로 엔티티를 리팩토링 하겠습니다.
Auditing 기능을 활용한 데이터 추적하기
AuditorAwarelmpl
package com.shop.confing;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Optional;
public class AuditorAwareImpl implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String userId = "";
if (authentication !=null){
userId = authentication.getName();
}
return Optional.of(userId);
}
}
1.현재 로그인 한 사용자의 정보를 조회하여 사용자의 이름을 등록자와 수정자로 지정합니다.
Auditing 기능을 사용하기 위해서 Confing 파일을 생성하겠습니다.
AuditConfing
package com.shop.confing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@Configuration
@EnableJpaAuditing
public class AuditConfig {
@Bean
public AuditorAware<String> auditorProvider(){
return new AuditorAwareImpl();
}
}
- JPA의 Auditing 기능을 활서화합니다.
- 등록자와 수정자를 처리해주는 AuditorAware을 빈으로 등록합니다.
보통 테이블에 등록일,수정일,등록자,수정자를 모두 다 넣어주지만 어떤 테이블을 등록자,수정자를 넣지 않는 테이블도 있을 수 있습니다. 그런 엔티티는 BaseTimeEntity만 상속받을 수 있도록 BaseTimeEntity 클래스를 생성합니다.
BaseTimeEntity
package com.shop.entity;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@EntityListeners(value = {AuditingEntityListener.class})
@MappedSuperclass
@Getter
@Setter
public class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime updateTime;
}
- Auditing을 적용하기 위해서 @EntityListeners어노테이션을 추가합니다
- 공통 매핑 정보가 필요할 때 사용하는 어노테이션으로 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공합니다.
- 엔티티가 생성되어 저장될 때 시간을 자동으로 저장합니다.
- 엔티티의 값을 변경할 때 시간을 자동으로 저장합니다.
BaseEntity는 위에서 만든 BaseTimeEntity를 상속받고 있습니다. 등록일,수정일 등록자,수정자를 모두 갖는 엔티티는 BaseEntity를 상속받으면 됩니다.
BaseEntity
package com.shop.entity;
import lombok.Getter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
@EntityListeners(value = {AuditingEntityListener.class})
@MappedSuperclass
@Getter
public abstract class BaseEntity extends BaseTimeEntity{
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String modifiedBy;
}
Member 엔티티에 Auditing 기능을 적용하기 위해서 BaseEntity 클래스를 상속받도록 하겠습니다.
Member
public class Member extends BaseEntity{
회원 엔티티 저장 시 자동으로 등록자,수정자,등록시간,수정시간이 저장되는 테스트 코드를 작성하겠습니다.
MemverTest
package com.shop.entity;
import com.shop.repository.MemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.TestPropertySource;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
@SpringBootTest
@Transactional
@TestPropertySource(locations = "classpath:application-test.properties")
public class MemberTest {
@Autowired
MemberRepository memberRepository;
@PersistenceContext
EntityManager em;
@Test
@DisplayName("Auditing 테스트")
@WithMockUser(username = "gildong", roles = "USER")
public void auditingTest(){
Member newMember = new Member();
memberRepository.save(newMember);
em.flush();
em.clear();
Member member = memberRepository.findById(newMember.getId())
.orElseThrow(EntityNotFoundException::new);
System.out.println("register time:" +member.getRegTime());
System.out.println("update time : " + member.getUpdateTime());
System.out.println("create member : " + member.getCreatedBy());
System.out.println("modify member : " + member.getModifiedBy());
}
}
1.스프링 시큐리티에서 제공하는 어노테이션으로 @WithMockUser에 지정한 사용자가 로그인한 상태라고 가정하고 테스트를 진행할 수 있습니다.
member 엔티티를 저장할 때 등록자나 등록일을 지정해주지 않았지만 저장 시간과 현재 로그인된 계정의 이름으로 지정된 것을 확인할 수 있습니다.
나머지 엔티티도 BaseEntity를 상속받도록 수정하겠습니다. 엔티티에 등록 시간 (regTime)과 수정 시간(update Time)이 맴버 변수로 있었다면 삭제후 상속합니다. 반복적인 내용이므로 OrderItem엔티티만 예시 코드로 작성하겠습니다. Cart,CartItem,Item,Order도 똑같이 수정합니다.
OrderItem
package com.shop.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
public class OrderItem extends BaseEntity{//<- 추가
@Id
@GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
private int orderPrice;//주문가격
private int count;// 수량
private LocalDateTime regTime; //삭제
private LocalDateTime updateTime; // 삭제
}
1. 기존에 있던 regTime, updateTime 변수를 삭제하고 BaseEntity를 상속받도록 소스코드를 수정합니다.
정말 중요한 내용인대 이해 되지 않는 부분이 있다면 다시 확인해야 합니다. 엔티티가 서로 복잡한 연관 관계를 맺을 때 성능 이슈가 있기도 합니다. 지연 로딩을 통해서 쿼리문이 필요할 때만 실행되도록 최적화를 하였습니다. 결국 성능 저하는 데이터를 저장하거나 수정할 때 일어나기보다는, 데이터를 조회할 때일어납니다. 다음은 조회할 때 최적화를 할 수 있는 방법이 있는지 방법을 찾아 보겠습니다.
'Spring > Spring JPA' 카테고리의 다른 글
영속성 전이 (0) | 2023.03.06 |
---|---|
연관 관계 매핑 종류 (1) | 2023.03.05 |
@Query (0) | 2023.03.02 |
엔티티 매핑 관련 어노테이션 (0) | 2023.02.28 |
JPA (0) | 2023.02.28 |