Programming-[Backend]/JPA

[spring data JPA] 3. 확장 : Auditing, Paging - @CreatedAt, @PageableDefault 등

컴퓨터 탐험가 찰리 2021. 11. 21. 04:09
728x90
반응형

 

 

1. 사용자 정의 레포지토리 구현

 

JpaRepository를 상속받는 Repository를 사용해야되는 경우, 내가 정의하는 메소드 외에 기본적으로 Spring Data JPA에서 제공하는 모든 메서드를 구현해야되서 사용이 힘들다.

 

따라서 내가 정의한 메서드만 Override 할 수 있도록 사용자 정의 레포지토리를 작성할 수 있다. 인터페이스를 MemberRepository 라고 하고, 그에 맞는 구현체는 MemberRepositoryImpl이라고 작성해주면, 스프링 부트가 알아서 사용자 정의 구현체임을 식별하여 Spring data JPA의 기능을 활용할 수 있도록 처리해준다. [인터페이스 이름 + Impl] 이라는 규칙을 적용해야되는 것을 기억하자. xml 이나 JavaConfig에서 설정을 바꾸어 Impl 대신 다른 postfix로 바꿀 수도 있으나, 굳이 권장하지는 않는다.

 


 

2. Auditing

 

Auditing은 엔티티를 생성, 변경할 때 변경한 사람과 시간을 자동으로 추적할 수 있게 해주는 기능이다. 운영상에 이력을 추적하기 위해서 필수적인 기능이다.

 

JPA 이용

JPA만 이용해서 생성 시각과 수정 시각을 기록하는 기능을 만들어본다. createdDate, updatedDate를 생성하고, 생성일자를 의미하는 createdDate는 수정이 불가능하도록 updatable = false 속성을 준다. @PrePersist는 실제 엔티티의 레코드가 persist되기 전에, @PreUpdate는 수정 전에 작동하도록 해주는 어노테이션이다. 이를 적용하여 메서드를 만들어 놓는다. 그리고 기본편에서 배운대로 @MappedSuperclass를 적용해야하는 것도 잊지말자. 참고로 @PostPersist, @PostUpdate 기능도 존재한다.

 

persist 부분에 updatedDate를 넣어서 생성 시에 updatedDate도 입력되도록 하는 것이 좋다. 만약 이 값이 null인채로 남아있으면 null 처리를 해줘야되므로 번거롭기 때문이다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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();
  }
}
cs

 

이렇게 생성한 엔티티를 사용하고자하는 엔티티에 상속해주면 된다.

1
public class Member extends JpaBaseEntity {
cs

 

 

 

Spring Data JPA 이용

 

Spring Data JPA는 @CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy를 사용하면 된다. 그리고 main 메서드를 실행하는 곳에 반드시 @EnableJpaAuditing을 붙여줘야 한다. BaseEntity로 지정하는 부분에서는 이벤트를 감지하는 기능을 사용한다는 의미로 @EntityListeneres(AuditingEntityListener.class) 어노테이션을 붙여줘야 한다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {
 
  @CreatedDate
  @Column(updatable = false)
  private LocalDateTime createdDate;
 
  @LastModifiedDate
  private LocalDateTime lastModifiedDate;
 
  @CreatedBy
  @Column(updatable = false)
  private String createdBy;
  
  @LastModifiedBy
  private String lastModifiedBy;
 
}
cs

 

시간이 아니라 ...By 계열의 정보를 입력하기 위해서 실무에서는 session 정보에서 사용자의 id 값을 꺼내온다. 예시를 위해서 여기서는 스프링의 AuditorAware를 사용해본다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
@EnableJpaRepositories(basePackages = "study.datajpa.repository")
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());
    }
 
}
cs

 

 

그리고 보통 시간에 대한 정보는 거의 모든 엔티티에서 사용하므로 BaseTimeEntity 등으로 따로 빼고, BaseEntity에서는 이를 상속받아서 개별적으로 사용하는 방식을 적용한다고 한다. 다시 말해 대부분의 엔티티에서는 BaseTimeEntity를 사용하고, 생성자, 수정자를 표시해야되는 엔티티에는 BaseEntity를 상속받는 식으로 사용하는 것이 좀 더 깔끔하다.

 

 


 

3. 페이징 Web 확장

 

다음과 같이 RestController를 만들고, @PostConstruct를 통해 초기 데이터를 만들어준 다음 웹에서 해당 url을 호출해보면, paging 형식의 json으로 결과가 출력되는 것을 확인할 수 있다.

 

기본으로 적용된 size는 20이므로 100개 데이터이면 20개씩 5페이지로 출력된다. 페이징은 0부터 시작한다는 것을 다시 한번 기억하자. 그리고 쿼리파라미터를 추가함으로써 paging과 sort 값을 넣어줄 수 있다.

 

localhost:8080/members?page=0&size=3&sort=id,desc&sort=username,desc

 

1
2
3
4
5
6
7
8
9
@GetMapping("/members")
  public Page<Member> list(Pageable pageable) {
    return memberRepository.findAll(pageable);
  }
 
  @PostConstruct
  public void init() {
    IntStream.range(0101).boxed().forEach(i -> memberRepository.save(new Member("user" + i, i)));
  }
cs

 

 

 

 

유의점

@PostConstruct 주석

@PostConstruct를 적용해놓으면, 해당 Member 엔티티가 생성될 때마다 작동하여 100개의 데이터가 만들어진다. 다른 로직이나 테스트에서 혼선이 올 수 있으므로 데이터가 꼭 필요하지 않다면 주석처리를 해놓자.

 

@Rollback(false)로 DB상 데이터 확인

@Rollback(false)를 실행 클래스쪽에 둬야 롤백이 일어나지 않아서 DB상의 데이터 확인이 가능하다.

 

 

 

 

페이지 설정

페이지 설정을 글로벌 환경설정으로 바꿔줄 수 있다. application.yml에 다음 설정을 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/datajpa
    username: sa
    password:
    driver-class-name: org.h2.Driver
 
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
#        show-sql: true
        format_sql: true
  
  data:
    web:
      pageable:
        default-page-size: 10
        max-page-size: 2000
cs

 

 

@PageableDefault

@PagebleDefault(size=5, sort = "username") 등의 옵션을 줄 수도 있다. 이 설정은 글로벌 설정보다 구체적이므로 글로벌 설정에 우선한다.

 

1
2
3
4
@GetMapping("/members")
  public Page<Member> list(@PageableDefault(size=5, sort="username") Pageable pageable) {
    return memberRepository.findAll(pageable);
  }
cs

 

 

 

 


 

참조

 

1. 인프런_실전! 스프링 부트와 JPA 활용1_김영한 님 강의

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-JPA-%EC%8B%A4%EC%A0%84/

728x90
반응형