Spring Data JPA #4 단순 게시글 처리 #3 @Query 애노테이션 이용하기

2021. 10. 6. 16:05JAVA/Spring

이전글

https://yonghwankim-dev.tistory.com/135

 

Spring Data JPA #4 단순 게시글 처리 #2 쿼리 메서드의 페이징 처리와 정렬

이전 글 https://yonghwankim-dev.tistory.com/134 Spring Data JPA #4 단순 게시글 처리 #1 쿼리 메서드 사용 본 글은 스타트 스프링 부트 도서의 내용을 복습하기 위해 작성된 글입니다. 개요 Spring Data JPA의..

yonghwankim-dev.tistory.com

 

본 글은 스타트 스프링 부트 도서의 내용을 복습하기 위해 작성된 글입니다.

 

개요

@Query 애노테이션을 활용하여 제목 및 내용에 대한 검색 처리를 수행하고 Paging 처리/정렬을 같이 수행할 수 있도록 실습합니다.

  • 쿼리 메소드라는 메소드의 이름만으로 원하는 SQL을 실행하는 방법
  • @Query를 이용한 좀 더 구체화된 JPQL 처리 (현재)
  • 페이징과 정렬에 대한 처리
  • Querydsl을 이용한 동적 쿼리

 

1. 단순 게시물의 처리를 위한 @Query 작성

1.1 제목에 대한 검색 처리

org.zerock.persistence.BoardRepository

public interface BoardRepository extends CrudRepository<Board, Long>{
	
	// b.bno > 0 조건으로 인해서 탐색 성능 높힘
	// 쿼리문에서 ?은 JDBC의 PreparedStatment 역할
	// LIKE 문에서 1은 첫번재 파라미터라는 의미, title 문자열이 LIKE문에 삽입됨
	@Query("SELECT b FROM Board b WHERE b.title LIKE %?1% AND b.bno > 0 ORDER BY b.bno DESC")
	public List<Board> findByTitle(String title);
	
}

@Query에는 JPQL(객체 쿼리)을 이용합니다. JPQL은 JPA에서 사용하는 Query Language입니다. SQL과 유사한 구문들로 구성되고, JPA의 구현체에서 이를 해석해서 실행합니다.

 

JPQL의 가장 기본적인 형태는 테이블(tbl_boards) 대신에 엔티티(Board) 타입을 이용한다는 것과 칼럼명 대신에 엔티티의 속성을 이용하는 것입니다.

 

org.zerock.Boot03ApplicationTests 일부, JPQL 테스트

	@Test
	void testByTitle2() {
		// 게시물 제목에 17이 포함되는 게시물 검색
		repo.findByTitle("17").forEach(board->System.out.println(board));
	}

 

1.2 내용에 대한 검색 처리 - @Param

내용에 대한 검색 처리 역시 제목에 대한 검색 처리와 동일한 방식으로 처리할 수 있습니다. @Query에는 @Param을 적용이 가능하여 적용할 수 있습니다.

 

org.zerock.persistence.BoardRepository

public interface BoardRepository extends CrudRepository<Board, Long>{

	// 게시물의 내용 검색
	// 내용에 대한 검색 처리 - @Param
	// @Param("content")로 설정된 content 문자열이 @Query 애노테이션의 LIKE문 :content에 매핑 됨
	@Query("SELECT b FROM Board b WHERE b.title LIKE %:content% AND b.bno > 0 ORDER BY b.bno DESC")
	public List<Board> findByContent(@Param("content") String content);

}

위의 작성된 코드를 보면 기존의 검색 처리 결과와 달리 '%:content%'와 같이 처리되는 것을 볼 수 있습니다. 파라미터에서는 @Param이라는 어노테이션을 이용하는 것을 볼 수 있습니다. @Param은 org.springframework.data.repository.query.Param 클래스를 이용합니다. 덕분에 여러 개의 파라미터를 전달할 때 이름을 이용해 쉽게 구분해서 전달이 가능합니다.

 

org.zerock.Boot03ApplicationTests 일부

	@Test
	void testByContent2() {
		// 게시물 내용에 17이 포함되는 게시물 검색
		repo.findByContent("17").forEach(board->System.out.println(board));
	}

 

2. @Query의 활용

@Query 애노테이션을 활용하여 얻을 수 있는 장점

  • 리턴 값이 반드시 엔티티 타입이 아니라 필요한 몇 개의 칼럼 값들만 추출 가능
  • nativeQuery 속성을 지정해서 데이터베이스에 사용하는 SQL을 그대로 사용할 수 있음
  • Repository에 지정된 엔티티 타입 뿐 아니라 필요한 엔티티 타입을 다양하게 사용할 수 있음

2.1 필요한 칼럼만 추출하는 경우

@Query를 이용하면 'select'로 시작하는 JPQL 구문을 작성하는데, 이때 필요한 칼럼만을 추출할 수 있습니다.

 

아래 예제는 Board 엔티티 타입에서 content 칼럼의 내용을 제외한 칼럼을 가져오는 예제입니다.

 

org.zerock.persistence.BoardRepository

public interface BoardRepository extends CrudRepository<Board, Long>{

	// 게시물의 제목 검색
	// 필요한 컬럼만 추출하는 경우 사용
	@Query("SELECT b.bno, b.title, b.writer, b.regdate FROM Board b WHERE b.title LIKE %?1% "
			+ "AND b.bno>0 ORDER BY b.bno DESC")
	public List<Object[]> findByTitle2(String title);
	
}

 

org.zerock.Boot03ApplicationTests 일부

	@Test
	void testByTitle17() {
		// 게시물의 제목이 17이 포함되는 게시물 검색
		// 필요한 컬럼만 추출하는 테스트, content 칼럼의 내용을 제외하고 검색
		repo.findByTitle2("17").forEach(arr->System.out.println(Arrays.toString(arr)));		
	}

여러 칼럼을 특정한 경우 리턴 타입은 엔티티 타입이 아니라 Object[]의 리스트 형태입니다. 때문에 Object[]로 반환되기 때문에 배열을 출력해야 합니다.

 

2.2 nativeQuery 사용

@Query는 말 그대로 데이터베이스에 종속적인 SQL문을 그대로 사용할 수 있기 때문에 복잡한 쿼리 작성시 유용하게 사용할 수 있습니다. 다만, 이 경우 데이터베이스 언어에 종속적이기 때문에 주의해야 합니다.

 

@Query 애노테이션에 nativeQuery 속성을 'true'라고 지정하면 메소드 실행 시 @Query의 value값을 그대로 실행하게 됩니다.

 

org.zerock.persistence.BoardRepository

public interface BoardRepository extends CrudRepository<Board, Long>{
		
	// 게시물의 제목 검색
	// nativeQuery 사용
	// nativeQuery 사용시(nativeQuery=true) @Query의 value값을 그대로 실행함
	@Query(value = "select bno, title, writer from tbl_boards where title like CONCAT('%',?1,'%') "
			+ " and bno>0 order by bno desc",nativeQuery = true)
	public List<Object[]> findByTitle3(String title);
	
}

org.zerock.Boot03ApplicationTests 일부

	@Test
	void testByTitle20() {
		// 게시물의 제목이 20이 포함되는 게시물 검색
		// nativeQuery 사용 테스트
		repo.findByTitle3("20").forEach(arr->System.out.println(Arrays.toString(arr)));
	}

 

3. @Query와 Paging 처리/정렬

@Query를 이용하더라도 페이징 처리를 하는 Pageable 인터페이스는 그대로 활용이 가능합니다. 만일 메서드의 파라미터에 Pageable 타입을 사용하게 되면 '@Query로 작성한 내용 + 페이징 처리'의 형태가 됩니다.

 

org.zerock.persistence.BoardRepository

public interface BoardRepository extends CrudRepository<Board, Long>{

	// 게시물 전체 검색
	// @Query와 Paging 처리/정렬
	// @Query로 작성한 내용 + 페이징 처리, 같이 수행 가능
	@Query("SELECT b FROM Board b WHERE b.bno>0 ORDER BY b.bno DESC")
	public List<Board> findBypage(Pageable pageable);
	
}

 

org.zerock.Boot03ApplicationTests 일부

	@Test
	void testByPaging() {
		// 한페이지에 10건의 데이터 페이징 처리하여 전체 게시물 검색
		Pageable paging = PageRequest.of(0, 10);
		
		repo.findBypage(paging).forEach(board->System.out.println(board));	
	}
Hibernate: select board0_.bno as bno1_0_, board0_.content as content2_0_, board0_.regdate as regdate3_0_, board0_.title as title4_0_, board0_.updatedate as updateda5_0_, board0_.writer as writer6_0_ from tbl_boards board0_ where board0_.bno>0 order by board0_.bno DESC limit ?
Board(bno=408, title=제목.. 200, writer=user00, content=내용...200 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=407, title=제목.. 199, writer=user09, content=내용...199 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=406, title=제목.. 198, writer=user08, content=내용...198 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=405, title=제목.. 197, writer=user07, content=내용...197 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=404, title=제목.. 196, writer=user06, content=내용...196 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=403, title=제목.. 195, writer=user05, content=내용...195 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=402, title=제목.. 194, writer=user04, content=내용...194 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=401, title=제목.. 193, writer=user03, content=내용...193 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=400, title=제목.. 192, writer=user02, content=내용...192 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)
Board(bno=399, title=제목.. 191, writer=user01, content=내용...191 채우기, regdate=2021-10-04 13:11:13.0, updatedate=2021-10-04 13:11:13.0)

 

References

스타트 스프링 부트, 구멍가게 코딩단 저