노트 정리/Spring Data JPA

[Spring Data JPA] 확장

DuL2 2023. 4. 25. 18:07

사용자 정의 레포지토리

 Spring Data JPA만을 사용해 Repository interface를 만들어 사용하게 되면 구현체는 Spring이 생성하므로 편리하지만 몇 가지 기능을 직접 구현하고 싶을 때는 개발자가 interface에 상속받는 `JpaRepository`의 모든 기능을 구현해주어야 하는 애로사항이 생긴다. JPA를 직접 사용하고 싶거나, 스프링 JDBC Temlate을 직접 사용하고 싶거나 MyBatis를 사용하거나 데이터베이스 커넥션을 직접 사용하는 등의 상황에서 구현체를 직접 만들고 싶을 때 사용자 정의 레포지토리를 사용할 수 있다.

 

사용자 정의 레포지토리 작성

 사용자 정의 레포지토리를 만들기 위해서는 다음과 같은 구조로 작성해야한다.

 먼저, 사용자 정의 메서드 작성을 위한 interface를 원하는 이름으로 작성하고 메서드를 작성한다.

 

 다음, 이를 구현하는 구현체의 클래스 이름을 `XXXImpl`로 JPA 관례상 정의하고 클래스에 메서드를 구현하면 된다.

 

 마지막으로, 사용자 정의 레포지토리를 사용할 MemberRepository에서 JpaRepository와 함께 MemberRepositoryCustom을 함꼐 extends 받으면 MemberRepository가 Spring에 의해 구현될 때 Impl이 접미사로 붙은 클래스를 찾아 구현하게 된다. 2.x 버전 부터는 사용자 정의 인터페이스인 MemberRepositoryCustom에 Impl을 붙여 작성해도 사용이 가능하다.

 

Impl 대신 커스텀 접미사를 사용하고 싶다면?

 XML 설정을 하거나 JavaConfig 설정을 통해 가능하다.

  <repositories base-package="study.datajpa.repository"
                repository-impl-postfix="Impl" />
@EnableJpaRepositories(basePackages = "study.datajpa.repository",
                           repositoryImplementationPostfix = "Impl")

 다만, 특별한 이유가 없다면 관례를 사용하는 것이 추후 내가 아닌 타인의 유지보수에 도움이 될 수 있다.

 

참고사항
 사용자 정의 레포지토리를 사용할 경우에는 잘 생각해보자. Repository 클래스를 용도에 맞게 분리하거나 복잡하게 구현하지 않고 단순 분리하여 작성할 수 있다는 사실을..

 

Auditing

 등록, 수정에 관한 정보를 거의 모든 Entity에 담아서 관리하면 추후 문제가 발생시 오류 추적이 쉬우므로 실무에서는 거의 필수로 사용한다고 한다. 그에 관한 설정을 알아보자.

 

순수 JPA 기반

 공통 속성이 들어갈 클래스를 만들어 작성해주고 `@MappedSuperclass` 어노테이션을 붙여 필드들을 상속 받을 수 있도록 하자.

@Getter
@MappedSuperclass
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 {
	...
}

 

주요 어노테이션

 사용할 수 있는 주요 어노테이션으로는 @PrePersist, @PostPersist @PreUpdate, @PostUpdate 이 있으며

 

 

[JPA이론] 7. 고급매핑

 

[JPA이론] 7. 고급매핑

상속관계 매핑 관계형 데이터베이스는 상속 관계를 매핑할 수 없다. 그나마 관계형 데이터베이스에는 슈퍼타입 - 서브타입 관계가 존재하여 객체의 상속과 비슷하므로 이를 사용해 객체의 상속

dul2.tistory.com

 

Spring Data JPA 기반

Spring Data JPA에서는 더욱 간단하게 설정이 가능하다. 먼저, `@EnableJpaAuditing` 어노테이션을 메인 어플리케이션 클래스에 붙여 JPA가 Auditing 기능을 사용할 수 있도록 해주어야 한다.

@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(DataJpaApplication.class, args);
    }

}

 

작성 및 수정 시간 넣기

 순수 JPA와 다른 어노테이션을 사용하게 된다. @CreatedDate @LastModifiedDate @CreatedBy @LastModifiedBy가 있다.

 

 추가적으로 Spring Data JPA는 Event 형식으로 Auditing을 인지하기 때문에 `@EntityListeners(AuditingEntityListener.class)`를 붙여주어야 한다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;
    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}

 

작성자 및 수정자 추가하기

 만약 작성자와 수정자 정보를 추가로 넣어야 한다면 다음과 같이 BaseTimeEntity를 상속받아 사용할 수도 있다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity extends BaseTimeEntity {

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

 이렇게 작성해놓은 후 엔티티에서 상속을 받게 되면 작성 및 수정에 대한 시간과 해당 활동을 한 회원 정보를 모두 담을 수 있게 된다. 하지만, 아직은 JPA가 작성자와 수정자에 대한 정보가 없기 때문에 `null`로 데이터가 들어가므로 Auditing을 위한 객체를 제공해 주어야 한다.

 

AuditiorAware 사용하기

 간단하게 사용하기 위해 Application 클래스에 Bean을 등록했으며 작성자와 수정자의 정보는 테스트를 위해 UUID를 만들어 작성하였다. 만약, 실제로 사용하게 된다면 로그인한 Session에서 정보를 받아와 사용하면 될 것이다.

@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(DataJpaApplication.class, args);
    }

    @Bean
    public AuditorAware<String> AuditorProvider() {
        return () -> Optional.of(UUID.randomUUID().toString());
    }
}

 AuditorProvide라는 클래스 명은 개발자가 임의로 작성한 것이며 원하는 이름으로 작성할 수 있다.

 

Web 확장 - 도메인 클래스 컨버터

 Spring이 HTTP 파라미터로 넘어온 엔티티 id를 받아 자동으로 엔티티 객체를 찾아서 바인딩 해주는 기술을 말한다. 코드를 통해 보면 이해가 쉬울 듯 하다.

@RestController
@RequiredArgsConstructor
public class MemberController {

    private final MemberRepository memberRepository;

    @GetMapping("/members/{id}")
    public String findMember(@PathVariable("id") Long id) {
        return memberRepository.findById(id).get().getUsername();
    }

    //도메인 클래스 컨버터
    @GetMapping("/members/{id}")
    public String findMember2(@PathVariable("id") Member member) {
        return member.getUsername();
    }

    @PostConstruct
    public void init() {
        memberRepository.save(Member.builder().username("user1").build());
    }
}

 findMember() 메서드는 기존의 id값을 받아 Repository에서 조회를 하게 된다. 하지만 findMember2() 메서드는 HTTP에서는 long 값이 넘어왔지만 바로 객체를 파라미터로 받아볼 수가 있는데 이는 중간에서 도메인 클래스 컨버터가 작동했기 때문이다.

 

 참고로 Spring의 경우에 이 기술을 쓰려면 다른 세팅이 더 필요하다.

 

Web 확장 - 페이징과 정렬

 Spring Data JPA는 Controller에서 HTTP 파라미터(요청 예시 : `/members?page=0&size=3&sort=id,desc&sort=username,desc`)에 page, size, sort를 받아 Pageable 인터페이스의 필드와 자동으로 바인딩 해준다. 

    @GetMapping("/members")
    public Page<Member> findMembers(Pageable pageable) {
        return memberRepository.findAll(pageable);
    }

Pageable 인터페이스는 PageRequest를 기본적인 구현체로 두고 있으며 실질적인 값은 PageRequest 객체가 들어가게 된다.

page, size, sort Default 설정

글로벌

application.xml 에 다음과 같이 글로벌하게 설정 가능하다.

spring:
  data:
    web:
      pageable:
        default-page-size: 20
        max-page-size: 2000

개별

    @GetMapping("/members")
    public Page<Member> findMembers(@PageableDefault(size = 12, sort = "username", direction = Sort.Direction.DESC) Pageable pageable) {
        return memberRepository.findAll(pageable);
    }

 

paging 처리

 

Spring - Paging 처리

Pagination 본인은 전통적인 서블릿 JSP 기반 프로젝트에서 페이지네이션을 하기 위에 List를 받아와 나누어 사용하는 Paging 알고리즘을 이용하여 해본 경험 뿐이 없었다. Pagination을 깔끔하고 이쁘게

dul2.tistory.com

 

'노트 정리 > Spring Data JPA' 카테고리의 다른 글

[Spring Data JPA] 나머지  (0) 2023.04.26
[Spring Data JPA] 쿼리  (0) 2023.04.23
[Spring Data JPA] 개요  (0) 2023.04.22