Spring boot & JPA

Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 3)

로기221 2023. 4. 5. 16:39
728x90
반응형

https://rogi221.tistory.com/174

 

Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 1)

연관 관계 매핑 - 1 연관 관계 매핑 종류 엔티티들은 대부분 다른 엔티티와 연관 관계를 맺으며, JPA에서는 엔티티에 연관 관계를 매핑해두고 필요할 때 해당 엔티티와 연관된 엔티티를 사용하여

rogi221.tistory.com

https://rogi221.tistory.com/175

 

Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 2)

https://rogi221.tistory.com/174 Spring Boot - 쇼핑몰 프로젝트 05 (연관 관계 매핑 - 1) 연관 관계 매핑 - 1 연관 관계 매핑 종류 엔티티들은 대부분 다른 엔티티와 연관 관계를 맺으며, JPA에서는 엔티티에 연

rogi221.tistory.com

 

 


연관 관계 매핑 - 3

 

 

 

Auditing을 이용한 엔티티 공통 속성 공통화

  • 지금까지 설계한 Item, Order, OrderItem 엔티티에는 등록시간(regTime), 수정시간(updateTime) 멤버변수가 공통
  • Spring Data Jpa에서는 Auditing 기능을 제공하여 엔티티가 저장 또는 수정될 때 자동으로 등록일, 수정일, 등록자, 수정자를 입력 가능
  • Audit의 사전적 정의는 ‘감시하다’  즉, 엔티티의 생성과 수정을 감시

 

 

  • 현재 로그인한 사용자의 정보를 등록자와 수정자로 지정하기 위해서 AuditorAware 인터페이스를 구현한 클래스를 생성

 

 

Auditing 기능을 활용한 데이터 추적하기

com.shop.config.AuditorAwareImpl.java

// AuditorAwareImpl.java

package com.shop.config;

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);
    }
}

 

 

 

  • AuditorAwareImpl 객체 Bean 등록

com.shop.config.AuditConfig.java

// AuditConfig.java

package com.shop.config;

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();
    }
}

 

 

com.shop.entity.BaseTimeEntity.java

// BaseTimeEntity.java

package com.shop.entity;

import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
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 abstract class BaseTimeEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime regTime;

    @LastModifiedDate
    private LocalDateTime updateTime;
}

 

 

com.shop.entity.BaseEntity.java

// BaseEntity.java

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 상속

com.shop.entity.Member.java

// Member.java

package com.shop.entity;

import com.shop.constant.Role;
import com.shop.dto.MemberFormDto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.persistence.*;

@Entity
@Table(name="member")
@Getter
@Setter
@ToString
public class Member extends BaseEntity {

... 생략 ...

 

 

 

com.shop.entity.MemberTest.java

// MemberTest.java

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());
    }
}

 

  • member 엔티티를 저장할 때 등록자나 등록일을 지정해주지 않았지만 저장 시간과 현재 로그인된 계정의 이름으로 저장된 것을 확인 가능

 

 

 

 

  • 나머지 엔티티도 BaseEntity를 상속받도록 수정
  • 엔티티에 등록 시간(regTime)과 수정 시간(updateTime)이 멤버 변수로 있었다면 삭제 후 상속
  • 반복적인 내용이므로 OrderItem 엔티티만 예시 코드로 작성
  • Cart, CartItem, Item, Order도 똑같이 수정

 

 

com.shop.entity.OrderItem.java

// OrderItem.java

package com.shop.entity;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@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;  // 수량
}

 

728x90
반응형