취미로 음악을 하는 개발자

[Spring Framework] 쿼리 메소드 (1) 본문

공대인/Spring[Boot]

[Spring Framework] 쿼리 메소드 (1)

영월특별시 2019. 10. 3. 22:47
728x90

JPA에서는 각 데이터베이스에 맞는 Dialect가 별도의 SQL에 대한 처리를 자동으로 처리해 주기 때문에 개발시 생산성을 향상시킬 수 있다. 반면, 조금 복잡한 쿼리를 작성하기 위해서는 데이터베이스를 대상으로 하는 SQL이 아니라 JPA에서 사용하는 것(Named Query, JPQL, Query dsl 등)을 알아야 한다.


Spring Data JPA는 이러한 번거로운 과정을 조금이라도 줄여줄 수 있는데 그 방법 중 하나가 쿼리 메소드이다.

쿼리 메소드메소드의 이름만으로 필요한 쿼리를 만들어 내는 기능으로, 네이밍 룰만 알고 있어도 사용할 수 있다.



프로젝트 생성



* 이전 'boot02' 프로젝트의 main 파일들을 그대로 가져오면 된다.

Spring Web, MySQL, lombok, JPA, Devtools

: Tomcat이 실행된 상태에서 코드를 수정하면 자동으로 재시작하는 기능



// application.properties

1
2
3
4
5
6
7
8
9
10
11
12
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/jpa_ex?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=user
spring.datasource.password=password
 
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
 
logging.level.org.hibernate=info
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package org.zerock;
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zerock.domain.Board;
import org.zerock.persistence.BoardRepository;
 
@RunWith(SpringRunner.class)
@SpringBootTest
public class Boot03ApplicationTests {
 
    @Autowired
    private BoardRepository repo;
    
    @Test
    public void testInsert200() {
        for (int i = 1; i <= 200; i++) {
            Board board = new Board();
            board.setTitle("제목.." + i);
            board.setContent("내용 ..." + i + " 채우기 ");
            board.setWriter("user0" + (i%10));
            repo.save(board);
        }
    }
}
cs


BoardRepository 인터페이스를 작성하고 test 클래스에 위의 더미 데이터를 생성하는 코드를 넣고 실행해준다.




쿼리 메소드


: Spring Data JPA에서 제공하는 것으로, 메소드의 이름만으로 원하는 질의를 실행할 수 있다. 단 여기서 쿼리라는 용어는 'select'에만 해당한다. 쿼리 메소드는 다음과 같은 단어들로 시작한다.


find..By..    read..By    query..By    get..By    count..By


예를 들어, find..By로 쿼리 메소드를 만든다면 find 뒤에 엔티티 타입을 지정한다. Board 클래스라면 findBoardBy.. 가 된다.

만일 중간에 엔티티 타입을 지정하지 않는다면 현재 실행하는 Repository의 타입 정보를 기준으로 동작한다.

By 뒤쪽에는 컬럼명을 이용해서 구성한다. 예를 들어, 제목으로 검색하고자 한다면 findBoardByTitle이 된다.


쿼리 메소드 리턴 타입은 Page<T>, Slice<T>, List<T>와 같은 Collection <T> 형태가 될 수 있다. 가장 많이 사용되는 것은 List<T>, Page<T> 타입을 이용하는 것이다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.zerock.persistence;
 
import java.util.Collection;
import java.util.List;
 
import org.springframework.data.repository.CrudRepository;
import org.zerock.domain.Board;
 
public interface BoardRepository extends CrudRepository<Board, Long>{
    public List<Board> findBoardByTitle(String title);
    public Collection<Board> findByWriter(String writer);
    
    // 작성자에 대한 Like % 키워드 %
    public Collection<Board> findByWriterContaining(String writer);
    // Or 조건의 처리
    public Collection<Board> findByTitleContainingOrContentContaining(
            String title, String content);
    // Title Like % ? % and Bno > ?
    public Collection<Board> findByTitleContainingAndBnoGreaterThan(
            String keyword, Long num);
    // Bno > ? Order by bno Desc
    public Collection<Board> findByBnoGreaterThanOrderByBnoDesc(Long bno);
}
cs


테스트 코드에서 사용해볼 쿼리 메소드를 미리 다 작성해놨다. 테스트 코드만 하나씩 살펴보면서 풀이할 것이다.

다음 코드를 테스트 클래스에 넣어준다.


1
2
3
4
5
6
7
8
9
10
11
    @Test
    public void testByTitle() {
//        before Java 8
//        List<Board> list = repo.findBoardByTitle("제목..177");
//        for (int i = 0, len = list.getBatchSize(); i < len; i++) {
//            System.out.println(list.get(i));
//        }
        
        // Java 8
        repo.findBoardByTitle("제목..177").forEach(board -> System.out.println(board));
    }
cs


testByTitle()을 실행하면 다음과 같은 출력이 나온다.


Board 클래스가 가리키는 테이블에서 제목 이름이 '제목..177'인 컬럼을 찾아 출력한 것이다.

코드를 보면 Java 8 이전의 코드와 이후의 코드가 나와있는데 8 버전부터 forEach문 한 줄로 처리할 수 있는 것 같다. (forEach, 람다식)


 아래의 표는 Spring Data JPA 문서에 나와있는 쿼리 메소드 목록을 가져온 것이다.

KeywordSampleJPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

IsEquals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNullNull

findByAge(Is)Null

… where x.age is null

IsNotNullNotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)


Comments