스프링 데이터 JPA

김주언·2022년 10월 28일
0

Spring Boot

목록 보기
8/8
post-thumbnail

엔티티 매핑과 리포지터리

엔티티 클래스 매핑

JPA는 엔티티를 통해서 데이터를 관리한다. 엔티티를 기준으로 테이블 자동 생성 또한 가능하다.

Board

package com.example.chap05.domain;

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

@Data
@Entity
public class Board {
    @Id @GeneratedValue
    private Long seq;
    private String title;
    private String writer;
    private String content;
    @Temporal(TemporalType.TIMESTAMP)
    private Date createDate;
    private Long cnt;
}

Repository 인터페이스

엔티티 작성 후 엔티티를 이용하여 CRUD 기능을 처리하는 Repository 인터페이스를 작성한다.
Repository는 기존의 DAO와 동일한 개념으로 비즈니스 클래스에서는 이 Repository를 이용하여 실질적인 데이터베이스 연동을 처리한다.
Repository는 스프링에서 제공하는 리포지토리 중 하나를 상속하여 작성한다.

  • CrudRepository : 기본적인 CRUD 기능 제공
  • PagingAndSortingRepository : 검색기능과 페이징 기능 제공
  • JpaRepository : 스프링 데이터 JPA의 추가 기능 사용

모든 인터페이스들은 공통적으로 두 개의 제네릭 타입을 지정한다.

CrudRepository<T,ID>
  • T : 엔티티의 클래스 타입
  • ID : 식별자의 타입

스프링 데이터 JPA를 사용하는 경우 인터페이스에 대한 별도의 구현 클래스를 작성하지 않아도 스프링부트가 내부적으로 인터페이스에 대한 구현 객체를 자동으로 생성해준다.

또한 JPA 단독 사용 시 데이터베이스 연동을 위해 사용했던 EntityManagerFactory와 같은 객체들도 스프링 데이터 JPA가 내부적으로 처리해준다.


CRUD 기능

CrudRepository 인터페이스가 제공하는 메소드들

메소드기능
count()모든 엔티티의 개수 리턴
delete(ID)식별키를 통한 삭제
delete(Iterable<? Extends T>)주어진 모든 엔티티 삭제
exists(ID)해당 식별 키를 가진 엔티티가 존재하는지 확인
findAllById(ID)모든 엔티티 목록을 리턴한다.
findAll(Iterable<ID>)해당 식별키를 가진 엔티티 목록을 리턴한다.
findById(ID)해당 식별 키에 해당하는 단일 엔티티 리턴
saveAll<Iterable>여러 엔티티들을 한번에 등록하고 수정한다.
save<S entity>하나의 엔티티를 등록하고 수정한다.



쿼리 메소드 사용하기

쿼리 메소드 기초

1) 쿼리 메소드?

일반적으로 JPA를 이용하여 목록 기능을 구현 시 JPQL을 이용한다. JPQL은 검색 대상이 테이블이 아닌 엔티티 인 것 주의한다.

스프링 JPA에서는 복잡한 JPQL을 메소드로 대신 처리할 수 있도록 쿼리메소드라는 기능을 제공한다.

쿼리 메소드란, 메소드의 이름으로 필요한 쿼리를 만들어주는 기능
특정한 네이밍 룰을 따라야지만 생성된다.

예시

검색하려는 엔티티에서 특정 변수의 값만 조회하기 - find

find + 엔티티 이름 + By + 변수 이름
ex ) findBoardByTitle() : Board 엔티티에서 title 값만 조회한다.

만약 현재 사용하는 Repository 인터페이스에 타입을 선언해두었다면 선언된 타입 기준으로 자동으로 엔티티 이름이 적용된다.

BoardRepository의 제너릭 타입이 <Board, Long> 일 경우
ex ) findByTitle : Board 엔티티에서 title 값만 조회한다.

쿼리 메소드의 리턴 타입 : 모두 Collection<T> 타입이다.

  • Page<T>
  • Slice<T> : 페이징 처리
  • List<T> : 단순 목록 검색 시 사용

2) 쿼리메소드 사용

    @Test
    public void testFindByTitle(){
        List<Board> boardList = boardRepository.findByTitle("테스트 제목10");

        System.out.println("----------------- 검색 결과 -------------------");
        for (Board board : boardList) {
            System.out.println(board.toString());
        }
    }

3) 쿼리 메소드 유형

키워드예시
AndfindByTitleAndContent
OrfindByTitleOrContent
BetweenfindByStartDateBetween
LessThanfindByAgeLessThan
LessThanEqualfindByAgeLessThanEqual
AfterfindByStartDateAfter
BeforefindByStartDateBefore
IsNullfindByTitleIsNull
IsNotNullfindByTitleIsNotNull
LikefindByTitleLike
StartingWithfindByTitleStartingWith
ContainingfindByTitleContaining

쿼리 메소드 활용

1) LIKE 연산자

게시글 내용에 특정 단어가 속한 글 목록 검색하기 → LIKE 연산자, Containing 키워드 사용

BoardRepository

public interface BoardRepository extends CrudRepository<Board, Long> {
    List<Board> findByTitle(String searchKeyword);
    List<Board> findByContentContaining(String searchKeyword);
}

테스트


    @Test
    public void testFindByContent(){
        List<Board> boardList = boardRepository.findByContentContaining("14");

        System.out.println("----------------- 검색 결과 -------------------");
        for (Board board : boardList) {
            System.out.println(board.toString());
        }

    }

2) 여러 조건 사용

여러 칼럼에 제약 조건 달기 → And 또는 Or 키워드 사용

BoardRepository

    List<Board> findByTitleContainingOrContentContaining(String title, String content);

두 개의 칼럼에 제약조건을 걸어줬기 때문에 매개변수도 두 개를 받아줘야한다.
근데 이름이 짱 긴데 어쩔 수 없슴

테스트

@Test
    public void testFindByTitleAndContent(){
        List<Board> boardList = boardRepository.findByTitleContainingOrContentContaining("14", "12");

        System.out.println("----------------- 검색 결과 -------------------");
        for (Board board : boardList) {
            System.out.println(board.toString());
        }
    }

작성된 쿼리는 아래와 같다.

WHERE 절에 두개의 조건이 OR 연산으로 결합되었다


3) 데이터 정렬

데이터를 정렬하여 조회 → OrderBy + 변수 + Asc / Des

BoardRepository

    List<Board> findByTitleContainingOrderBySeqDesc(String title);



페이징과 정렬 처리

모든 쿼리 메소드는 마지막 파라미터로 페이징 처리를 위한 Pageable 인터페이스와 Sort 인터페이스를 추가할 수 있다

1) 페이징 처리

BoardRepository

    List<Board> findByTitleContaining(String title, Pageable paging);

테스트

    @Test
    public void testPaging(){
        Pageable paging = PageRequest.of(0,5);
        List<Board> boardList = boardRepository.findByTitleContaining("제목", paging);

        System.out.println("----------------- 검색 결과 -------------------");
        for (Board board : boardList) {
            System.out.println(board.toString());
        }
    }
  • PageRequest.of(0,5);
    • 첫번째 인자 : 페이지 번호
    • 두번째 인자 : 검색할 데이터의 개수

2) 정렬 처리

Sort 클래스 사용

Pageable paging = PageRequest.of(0,5, Sort.Direction.DESC, "seq");

Pageable 객체 생성 시 파라미터를 추가하여 준다.
첫번째는 정렬 방향에 대한 정보, 두 번째는 정렬 대상이 되는 변수 이름



Page<T> 타입

검색 결과는 List<T> 타입으로 받아도 되지만 Page<T> 타입으로 받으면 검색 결과를 활용하기 더 쉽다. 페이징 처리 시 사용할 수 있는 다양한 정보를 추가로 제공한다.

Page 객체가 제공하는 정보

메소드설명
getNumber()현재 페이지 정보
getSize()한 페이지의 크기
hasPreviousPage이전 페이지 존재 여부
hasNextPage다음 페이지 존재 여부

이거말고도 아무튼 많다





@Query 어노테이션

JPQL을 사용하거나 특정 데이터베이스에 종속적인 네이티브 쿼리를 사용하는 경우를 위해 제공되는 어노테이션. 쿼리메소드와 동일하게 페이징 처리와 정렬 처리가 가능하다

JPQL에서는 사용자 입력 값을 바인딩할 수 있도록 위치기반 파라미터와 이름 기반 파라미터 두 가지를 지원한다.

1) 위치기반 파라미터

'?[파라미터의 위치]' 형태로 사용한다. ('?1'의 경우 1번째 파라미터를 말한다.)

BoardRepository

@Query("SELECT b FROM Board b WHERE b.title LIKE %?1% ORDER BY b.seq DESC")
    List<Board> queryAnnotation1(String searchKeyword);
  • @Query 어노테이션을 이용하여 JPQL을 추가한다.
  • ?1 : 파라미터 위치 지정

2) 이름기반 파라미터

BoardRepository

    @Query("SELECT b FROM Board b WHERE b.title LIKE %:searchKeyword% ORDER BY b.seq DESC")
    List<Board> queryAnnotation1(@Param("searchKeyword") String searchKeyword);
  • @Param : 매개변수로 받은 String searchKeyword 값을 바인딩한다.


특정 변수만 조회

특정 변수 값만 조회할 경우 검색 결과로 엔티티 객체가 조회되는 것이 아니라 여러 변수값이 조회되는 것이기 때문에, 반환값을 List<Object[]> 형태로 받아야 한다.

	@Query("SELECT b.seq, b.title FROM Board b WHERE b.title like %?1%")
    List<Object[]> queryAnnotation2(@Param("searchKeyword") String searchKeyword);


페이징 처리

BoardRepository

	@Query("SELECT b FROM Board b")
    List<Board> queryAnnotation3(Pageable paging);

테스트

    @Test
    public void testQueryAnnotation3(){
        Pageable paging = PageRequest.of(0,3, Sort.Direction.DESC, "seq");
        List<Board> boardList = boardRepository.queryAnnotation3(paging);

        System.out.println("----------------- 검색 결과 -------------------");
        for(Board board : boardList) System.out.println(board.toString());
    }
profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글