JPA 강의 (사용자 정의 리포지토리, Auditing)

minyeob·2023년 4월 8일
0

Spring

목록 보기
8/11

스프링 데이터 JPA 강의 정리

확장 기능

사용자 정의 리포지토리 구현

  • 스프링 데이터 JPA 리포지토리는 인터페이스만 정의하고 구현체는 스프링이 자동 생성
  • 스프링 데이터 JPA가 제공하는 인터페이스를 직접 구현하면 구현해야 하는 기능이 너무 많음
  • 다양한 이유로 인터페이스의 메서드를 직접 구현하고 싶다면?
    • JPA 직접 사용
    • 스프링 JDBC Template 사용
    • MyBatis 사용
    • 데이터베이스 커넥션 직접 사용 등등...
    • Querydsl 사용(가장 많이 씀)

MemberRespositoryCustom 인터페이스

public interface MemberRepositoryCustom {
 List<Member> findMemberCustom();
}

MemberRespositoryCustom 구현 클래스

@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {
 private final EntityManager em;
 @Override
 public List<Member> findMemberCustom() {
 return em.createQuery("select m from Member m")
 .getResultList();
 }
}

사용자 정의 구현 클래스

  • 규칙: 리포지토리 인터페이스 이름 + Impl (지켜야 함)
  • 스프링 데이터 JPA가 인식해서 스프링 빈으로 등록

MemberRepository 에서 상속

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
    ...
}

사용자 정의 메서드 호출

List<Member> result = memberRepository.findMemberCustom();

이렇게 직접 커스텀한 코드를 작성하고 다음과 같이 호출을 하게 되면 구현 클래스의 코드가 실행된다.

참고

실무에서는 주로 QueryDSL이나 SpringJdbcTemplate을 함께 사용할 때 사용자 정의
리포지토리 기능 자주 사용

핵심 비즈니스 로직에 관련되면 사용자 정의 인터페이스를 만들어서 상속하면 되지만 핵심 비즈니스 로직에 관련이 없고 특정 화면에서 사용되는 경우라면   
인터페이스가 아닌 클래스로 만들어서 스프링 빈으로 등록해 사용하는 것이 좋다. 위에서 예시로 든 사용자 정의 인터페이스가 아닌 클래스로 만들면 다음과 같다.

Auditing(감시,추적)

  • 엔티티를 생성, 변경할 때 변경한 사람과 시간을 추적하고 싶으면?
    • 등록일
    • 수정일
    • 등록자
    • 수정자

순수 JPA 이용

@MappedSuperclass
@Getter
public class JpaBaseEntity {

    @Column(updatable = false)
    private LocalDateTime createdDate;
    private LocalDateTime updatedDate;

    @PrePersist
    public void prePersist() {
        LocalDateTime now = LocalDateTime.now();
        createdDate = now;
        updatedDate = now;
    }


    @PreUpdate
    public void preUpdate() {
        updatedDate = LocalDateTime.now();
    }
}

--------------------------------------------------
public class Member extends JpaBaseEntity {} //Member 엔티티에 추가

확인 코드

@Test
public void JpaEventBaseEntity() throws Exception {
        //given
        Member member = new Member("member1");
        memberRepository.save(member); // @PrePersist

        Thread.sleep(100);
        member.setUsername("member2");

        em.flush(); //@PreUpdate
        em.clear();


        //when
        Member findMember = memberRepository.findById(member.getId()).get();


        //then
        System.out.println("findMember.getCreatedDate = " + findMember.getCreatedDate());
        System.out.println("findMember.getUpdatedDate = " + findMember.getUpdatedDate());
}

스프링 데이터 JPA 를 사용하면 이것보다 더 편리하게 이용가능하다.

스프링 데이터 JPA 사용 - 등록일, 수정일

@EnableJpaAuditing  // 추가
@SpringBootApplication
public class DataJpaApplication {
@EntityListeners(AuditingEntityListener.class) //이벤트를 기반으로 동작하기 때문에 넣어줘야 한다.
@MappedSuperclass
@Getter
public class BaseEntity {

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

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}

스프링 데이터 JPA 사용 - 등록자, 수정자

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseEntity {

  ....
    
 @CreatedBy
 @Column(updatable = false)
 private String createdBy;
  
 @LastModifiedBy
 private String lastModifiedBy;
@EnableJpaAuditing  // 추가
@SpringBootApplication
public class DataJpaApplication {
    
  ...
@Bean
public AuditorAware<String> auditorProvider() {
 return () -> Optional.of(UUID.randomUUID().toString());
}

참고 : 저장시점에 등록일, 등록자는 물론이고, 수정일, 수정자도 같은 데이터가 저장된다. 데이터가 중복 저장되는 것 같지만, 이렇게 해두면 변경 컬럼만 확인해도 마지막에 업데이트한 유저를 확인 할 수 있으므로 유지보수 관점에서 편리하다.

추가)

시간에 대한 데이터는 대부분의 테이블이 다 사용하지만 등록자,수정자는 필요 없는 경우가 있다
따라서 이를 BaseTimeEntity(시간)와 그것을 상속받는 BaseEntity 로 분리해주면 유용하다.

@EntityListeners(AuditingEntityListener.class) //이벤트를 기반으로 동작하기 때문에 넣어줘야 한다.
@MappedSuperclass
@Getter
public class BaseTimeEntity {

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

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}
@EntityListeners(AuditingEntityListener.class) //이벤트를 기반으로 동작하기 때문에 넣어줘야 한다.
@MappedSuperclass
@Getter
public class BaseEntity extends BaseTimeEntity {

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
}

오늘의 한마디

하루에 해나갈수 있는 할당량을 점점 늘려 나가자.
profile
백엔드 개발자를 꿈꾸며 공부한 내용을 기록하고 있습니다.

0개의 댓글