[Spring] JPA, 페이징, 연관관계

스프링 JPA, 페이징, 연관관계

스파르타 코딩 클럽의 Spring 심화반 4주 차 내용인 스프링 JPA, 페이징, 연관관계를 정리한다.

핵심 내용

  • Spring Data JPA
  • 페이징
  • JPA의 연관 관계

Spring Data JPA

  • ORM : ORM은 Object-Relation Mapping이며 말 그대로 객체(여기선 자바)와 DB(H2, MySQL)를 이어주는 기술이다.
    • SQL의 작성 중 실수하기도 쉽고 개발자가 비즈니스 로직 개발보다 SQL 작성에 더 많은 노력을 들어야 했음
    • 객체지향과 관계형 DB는 사용 목적 및 사용 방법이 애초에 다름
    • 이를 해결하기 위해 ORM이 필요
  • JPA : JPA는 Java Persistience API로써 자바 ORM 기술에 대한 표준 명세이다.
    • 하이버네이트 : JPA는 표준 명세이고 이를 실제 구현한 프레임워크가 필요한데, 이 중 하이버네이트가 사실상 표준이다.
  • Spring Data JPA : Repository 인터페이스를 통해 필요한 구현은 스프링이 대신 해주는, JPA를 편리하게 사용하기 위해 매핑한 것

    • Spring Data JPA 예시 코드
      @Entity
      public class Product extends Timestamped {
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Id
        private Long id;
        private Long userId;
        private String title;
        private String image;
        private String link;
        private int lprice;
        private int myprice;
      }
      


      public interface ProductRepository extends JpaRepository<Product, Long> {
      }
    

페이징

  • 한 번에 수백 ~ 수억 개의 데이터를 날리면 서버나 클라이언트나 과부화 -> 나눠서 보내야됌
  • 페이지네이션(구글 검색), 인피니트 스크롤(유투브) 등이 있음

설계 (Server에서)

  • number : 조회할 페이지 번호 (0부터 시작)
  • size : 한 페이지에 보여줄 자료의 개수
  • numberOfElements: 실제 조회된 자료의 개수
  • totalElement : 전체 자료 개수
  • totalPages : 전체 페이지 수
  • first : 첫 페이지인지? (boolean)
  • last : 마지막 페이지인지? (boolean)

구현

Controller

@GetMapping("/api/products")
    public Page<Product> getProducts(
            @RequestParam("page") int page,
            @RequestParam("size") int size,
            @RequestParam("sortBy") String sortBy,
            @RequestParam("isAsc") boolean isAsc,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        Long userId = userDetails.getUser().getId();
        page = page - 1;
        return productService.getProducts(userId, page , size, sortBy, isAsc);
    }

파라미터를 통해 클라이언트가 원하는 페이지 수, 페이지당 사이즈, 배열에 대한 옵션을 받는다.

Service

public Page<Product> getProducts(Long userId, int page, int size, String sortBy, boolean isAsc) {
        Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);

        return productRepository.findAllByUserId(userId, pageable);
    }

컨트롤러에서 받은 파라미터를 이용하여 Sort로 통해 Pageable 객체를 생성할 때 필요한 파라미터를 만들어준다.

Repository

public interface ProductRepository extends JpaRepository<Product, Long> {
    Page<Product> findAllByUserId(Long userId, Pageable pageable);
}

Page<Product>를 반환형으로, Pageable 객체가 파라미터로 들어가는 메소드를 리포지터리에서 만들어준다.

JPA의 연관관계

  • JPA는 Entity 클래스의 필드 위에 연관관계 어노테이션(@)을 설정해 주는 것만으로 연관관계가 형성된다.
  • 어노테이션을 통해 객체끼리의 관계를 맺어주면, DB에서는 외래키를 통한 관계를 형성해준다.

구현에 쓰이는 어노테이션

  • @OneToMany : 일대다 관계
    • ex) 회원 1명이 여러 개의 폴더를 가질 수 있음
  • @OneToOne : 일대일 관계
    • ex) 배달 주문 1개 주문 시, 쿠폰 1개만 할인 적용 가능
  • @ManyToOne : 다대일 관계
    • ex) 폴더 여러 개를 회원 1명이 가질 수 있음
  • @ManyToMany : 다대다 관계
    • ex) 고객은 음식점 여러개 찜 가능, 음식점은 고객 여러명에게 찜 가능

ManyToMany

고객과 음식점과 같은 다대다 관계에서는 고객의 ID, 음식점의 ID와 다른 부가적인 정보를 담은 테이블을 따로 만들어줘야한다.
@ManyToMany 어노테이션으로 생성 시, JPA가 생성해준다.

연관관계 칼럼 설정

@ManyToOne
@JoinColumn(name = "USER_ID", nullable = false)
private User user;
  • name: 외래키 명
  • nullable: 외래키 null 허용 여부