페이징/정렬 처리하기

jadive study·2022년 11월 18일
0

스프링과 Hibernate
try〜 catch와 같은 많은 양의 코드를 직접 작성

Spring Data JPA
내부적으 로 클래스를 생성하는 방식으로 동작하면서 코드가 없는 형식의 개발이 가능

페이징 처리와 정렬은 전통적으로 SQL을 공부하는데 반드시 필요
오라클은 '인 라인 뷰(inline view)',mySQL은 limit

JPA는 내부적으로 이런 처리를 'Dialect과는 존재를 이용해서 처리
JDBC 정보가 예제와 같이 MariaDB의 경우에는 자동으로 MariaDB를 위한 Dialect가 설정
application.properties 등을 이용해서 설정
JPA가 이처럼 실제 데이터베이스에서 나용하는 SQL의 처리를 자동화
SQL이 아닌 API의 객체와 메 니드를 사용하는 형태로 페이징 처리

spring Data JPA에서 페이징 처리와 정렬은 특이하게도 findAll이라는 메서드를 사용 합니다.
findAll() JpaRepositoiy 인터페이스의 상위인 PagingAndSortRepository의 메서드로 파라미터로 전달되는 Pageable이라는 타입의 객체에 의해서 실행되는 쿼리를 결정

2.5.1 Pageable 인터페이스

org.springframework.data.domain.Pageable

Pageable 인터페이스

필요한 정보를 전달하는 용도의 타입으로 인터페이스이기 때문에 실제 객체를 생성
자는 특이하게도 protected로 선언, new이용할 수 없다. 객체를 생성하기 위해서는 static한 of()를 이용해서 처리.
PageRequest 생성자를 보면 page, size, Sort라는 정보를 이용해서 객체를 생성

static 메서드인 of()

• of (int page, int size): 0부터 시작하는 페이지 번호와 개수(size), 정렬이 지정되지 않음
• of (int page, int size, Sort.Direction direction. String.-props): 0부터 시작하는 페이지 번호와 개 수, 정렬의 방향과 정렬 기준 필드들
• of (int page, int size. Sort sort): 페이지 번호와 개수, 정렬 관련 정보

2.5.2 페이징 처리

Spring Data JPA를 이용할 때 페이지 처리는 반드시 '0'부터 시작한다는 점

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data•domain.Pageable; ...생략
@Test
public void testPageDefault() {
//I 페이지 10개
Pageable pageable = PageRequest.of(0,10);
Page<Memo> result = memoRepository.findAll(pageable); System.out.printin(result);
  }

테스트 코드에서는 PageRequest.of()를 이용해서 1페이지의 데이터 10개를 가 져오기 위해 파라미터로 ’0,W’을 전달

리턴 타입이 org.springfrarnework.data.domain.Page라는 점입니다. Page 타입이 흥미로운 이유는 단순히 해당 목록만으로 가져오는데 그치지 않고,실제 페이지 처리에 필요한 전체 데이터의 개수를 가져오는 쿼리 역시 같이 처리

스트 코드를 실행해 보면 다음과 같은 SQL이 실행

Hibernate:
select
memo0__.mno as mnol_0_?
memo0_. memo_text as memo_tex2__0_ from
tbl_memo memo0_ limit ?
Hibernate: select
count(memo0_.mno) as col__0__0_ from
tbl__memo memo0_

첫 번째 쿼리에서는 MariaDB에서 페이징 처리에 사용하는 limit 구 문이 사용
두 번째 쿼리에서는 count()를 이용해서 전체 개수를 처리
->findAlK)에 Pageable 타입의 파라미터를 전달,페이징 처리에 관련된 쿼리들을 실행,리턴 타입으로 지정 된 Page〈엔티티 타입〉객체로 저장

실제 페이지의 데이터 처리 getContent()를 이용해서 List〈엔티티 타입〉으로 처리하거나 Stream〈엔티티 타 입〉을 반환하는 get()을 이용

System .out. print In (**.....................................................................................");
for (Memo memo : result.getContent()) { System.out.println(memo);
}

2.5.3 정렬 조건 추가하기
페이징 처리를 담당하는 PageRequest에는 정렬과 관련된 org.springframework.data. domain.Sort 타입을 파라미터로 전달

MemoRepositoryTests 클래스의 일부 @Test
public void testSort() {
Sort sort1 = Sort.by("mno").descending();

testSort()에서는 가장 필요한 mno 필드의 값을 역순(desc)으로 정 렬하도 녹 Sort.by(필 드).desending()을 이용하는 것을 볼 수 있습니다. testSort()를 통해서 실행되는 쿼리를 보면 아래와 같이 정렬

정렬 조건은 Sort 객체의 and()를 이용해서 여러 개의 정렬 조건을 다르게 지정할 수 있 습니다. 예를 들어 Memo 클래스의 memoText는 asc로 하고. mno는 desc로 지정하고 싶다면 다음과 같은 형태로 작성

Sort sortl = Sort.by("mno").descending();
Sort sort2 = Sort.by("memoTextw).ascending();
Sort sortAll = sort1.and(sort2); //and롤 이용한 연결
Pageable pageable = PageRequest.of(0, 10, sortAll); //결합된 정렬 조건 사용

2.6 쿼리 메서드 (Query Methods) 기능과 @Query

JpaRepository의 마지막으로 살펴볼 기능은 쿼리 메서드라는 기능과JPQL(]ava Persistence Query Language)이라고 불리는 객체 지향 쿼리에 대한 기능

• 쿼리 메서드: 메서드의 이름 자체가 쿼리의 구문으로 처리되는 기능
• @Query: SQL과 유사하게 엔티티 클래스의 정보를 이용해서 쿼리를 작성하는 기능
• Querydsl 둥의 동적 쿼리 처리 기능

2.6.1쿼리 메서드(Query Methods)

쿼리 메서드는 말 그대로 '메서드의 이름 자체가 질의(query)문'이 되는 흥미로운 기능
리 메서드는 주로 'findBy나 getBy••••로 시작하고 이후에 필요한 필드 조건이나 And, On,Between 와 같은 키워드를 이용해서 메서드의 이름 자체로 질의 조건

쿼리 메서드는 사용하는 키워드 따라서 파라미터의 개수가 결정됩니다. 예를 들어 'Between* 의 경우에는문서에 다음과같이 설명
'?1,?2'와 같이 해당 키워드는 두 개의 변수가 필요

• select룰 하는 작업이라면 List 타입이나 배열을 이용할 수 있다.
• 파라미터에 Pageable 타입을 넣는 경우에는 무조건 Page 타입

package org.zerock.ex2.repository;
import erg.springframework.data.jpa.repository.JpaRepository;
import org.zerock.ex2.entity.Memo;
import java.util,List;
public interface MemoRepository extends 3paRepository<Memo, Long> {
Llst<Memo> findByMnoBetweenOrderByMnoDesc(Long from, Long to); }

메서드의 이름을 보면 mno를 기준으로 해서 between 구문을 사용하고 order by 가 적용될 것

MemoRepositoryTests 클래스의 일부

@Test
public void testQueryMethods(){
List<Memo> list = memoRepository. findByMnoBetweenOrderByMnoDesc(70L,80L);
for (Memo memo : list) { System, oi/t.println (memo);
} }


-> 테스트 코드의 결과

쿼리 메서드와 Pageable 의 결합

목록을 원하는 쿼리를 실행해야 한다면 대부분의 경우 앞의 예제와 같이 'OrderBy' 키워드 등을 사용
Pageable 파라미터를 같이 결합해서 사용할 수 있기 때문에 정 렬에 관련 부분은Pagable로 처리

MemoRepository 인터페이스의 변경

package org•zerock.ex2•repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.ex2.entity.Memo;
import java.util.Listj
public interface MemoRepository extends JpaRepositorycMemOj Long> {
  ListMemofindByMnoBetweenOrderByMnoDesc(Long from, Long to); Page<Memo> findByMnoBetween(Long from, Long to. Pageable pageable);}
  

정렬 조건은 Pageable을 통해서 조절할수 있기 때문에 좀 더 간단한 형태의 메서드 선언 이 가능
Pageable 파라미터는 모든 쿼리 메서드에 적용할 수 있으므로 일반적인 경우에는 쿼리 메서드에 정렬 조건은 생략하고 만드는 경우가 많습니다.
새롭게 추가된 findByMnoBet\veen()의 테스트 코드는 아래와 같다

MemoRepositoryTests

@Test
public void testQueryMethodWithPagable() {
Pageable pageable = PageRequest.of(0, 10, Sort.byC'mno").descending()); Page<Memo> result = memoRepository.findByMnoBetween(10L50L, pageable); result.get().forEach(memo -> System.out.printIn(memo));
} 

쿼리 메서드와 Pageable 의 결합

목록을 원하는 쿼리를 실행해야 한다면 대부분의 경우 앞의 예제와 같이 'OrderBy' 키워드 등을 사용해야 하기 때문에 메서드의 이름도 길어지고 혼동하기도 쉽습니다.
다행 스럽게도 쿼리 메서드는 Pageable 파라미터를 같이 결합해서 사용할 수 있기 때문에 정 렬에 관련 부분은Pagable로 처리해서 좀 더 간략한메서드를생성

MemoRepository 인터페이스의 변경

package org•zerock.ex2•repository;
importorg.springframework.data.domain.Page;
importorg.springframework.data.domain.Pageable;
importorg.springframework.data.jpa.repository.JpaRepository;
import org.zerock.ex2.entity.Memo;
import java.util.List;
public interface MemoRepository extends JpaRepository<Memo, Long> {ist〈MemofindByMnoBetweenOrderByMnoDesc(Long from, Long to); Page<Memo> findByMnoBetween(Long from, Long to. Pageable pageable);}

MemoRepositor/tests 클래스의 일부

@Test
public void testQueryMethodWithPagable() {
Pageable pageable = PageRequest.of(0, 10, Sort.byC'mno").descending()); Page<Memo> result= memoRepository.findByMnoBetween(10L50L, pageable); result.get().forEach(memo -> System.out.printIn(memo));
}

Hibernate:
select
memo0.mno as mnol_0,
memo0.memo_text as memo_tex2_0 from
memo memo0 where
memo0__.mno between ? and ? order by
memo0
.mno desc limit ? Hibernate:
select
count(memo0.mno) as col_0_0
from
memo memo0 where
memo0
.mno between ? and ?
Memo(mno=50, memoText=Sample..50)
Memo(mno=49, memoText=Sample...49)
Memo(mno=48, memoText=Sample...48)
Memo(mno=47, memoText=Sample.•-47)
Memo(mno=46, memoText=Sample...46)
Memo(mno=45, memoText=Sample...45)
Memo(mno=44, memoText=Sample...44)
Memo(mno=43, memoText=Sample...43)
Memo(mno=42, memoText=Sample...42)
Memo(mno=41, memoText=Sample...41)

deleteBy 로 시작하는 삭제 처리
쿼리 메서드를 이용해서 'deleteBy'로 메서드의 이름을 시작하면 특정한 조건에 맞는 데이 터를 삭제하는 것도 가능합니다.
Intellij의 경우 쿼리 메서드를 작성하는 기능을 지원하므 로좀 더 수월하게 작성

메모의 번호(mno)가 10보다 작은 데이터를 삭제한다면 다음과 같이 작성할수 있습니다.

public interface MemoRepository extends JpaRepositorycMemo, Long> {
void deleteMemoByMnoLessThan(Long num); }  

test code

@Commit
@Transactional
@Test
public void testDeleteQueryMethods() {
memoRepository.deleteMemoByMnoLessThan(10L); }

@Transactional과 @Commit

-@Commit은 최종 결과를 커밋하기 위해서 사용합니다. 이를 적용하지 않으면 테스트 코 드의 deleteBy..는 기본적으로 롤백(Rollback) 처리되어서 결과가 반영되지 않습니다.

-deleteBy는 실제 개발에는 많이 사용되지 않는데 그 이유는 SQL을 이용하듯이 한 번에 삭제가 이루어지는 것이 아니라 아래와같이 각 엔티티 객체를하나씩 삭제하기 때문입니다.

2.6.2 @Query 어노테이션

Spring Data JPA가 제공하는 쿼리 메서드는 검색과 같은 기능을 작성할 때 편리함을 제공
'And,Or' 등이 사용되면서 조금 불편할 때가 많습니다. 때문에 일반적인 경우에는 간단한 처리만 쿼리 메서드를 이용하고,@Query를 이용
@Query의 value는 JPQL(Java Persistence Query Language) 로 작성하는데 흔히 '객체지향 쿼리'라고 불리는 구문

• 필요한 데이터만 선별적으로 추출하는 기능이 가능
• 데이터베이스에 맞는 순수한 SQL(Native SQ나을 사용하는,I능
• insert, update, delete와 같은 select가 아닌 DML 등을 처리하는 기능(@Modifying과 함께 사용)

객체지향 쿼리

테이블 대신에 엔티티 클래스를 이용하고,테이블의 칼럼 대신에 클래스 에 선언된 필드를 이용해서 작성합니다. JPQL은 SQL과 상당히 유사하기 때문에 간단한 기능을 제작하는 경우에는 추가적인 학습 없이도 적용이 가눙

@Query("select m from Memo m order by m.mno desc") List<Memo> getListDesc();

Jpql은 데이터베이스의 테이블 대신에 엔티 티 클래스와 멤버 변수를 이용해서 SQL과 비슷한 JPQL을 작성

@Query 의 파라미터 바인딩

@Query의 경우 직접 SQL과 유사한 형태로 작성되기 때문에 'where' 구문과 그에 맞는 파라미터들을 처리할 때가 많습니다. 이때는 다음과 같은 방식을 사용

• ?1, ?2' 와 1부터 시작하는 파라미터의 순서를 이용하는 방식
• ':xxx' 와 같이 파라미터 이름을 활용하는 방식
• '#()'과 같이 자바 빈 스타일을 이용하는 방식

@Query("update Memo m set m.memoText = :memoText where m.mno = :mno ") int updateMemoText(@Param("mno") Long mno, @Param("memoText") String memoText );
  

Native SQL 처리

-@Query의 강력한 기능은 데이터베이스 고유의 SQL 구문을 그대로 활용하는 것
-JPA 자체가 데이터베이스에 독립적으로 구현이 가능하다는 장점을 잃어버 리기는 하지만 경우에 따라서는 복잡한 JOIN 구문 등을 처리하기 위해서 어쩔 수 없는 선택을 하는 경우에 사용

@Query(value = "select * from memo where mo > 0", nativeQuery = true) List<Object[]> getNativeResult();

@Query의 nativeQuery 속성값을 true로 지정하고 일반 SQL을 그대로 사용할 수 있다.

profile
개발 메모창고

0개의 댓글