Spring발행일 2024. 8. 21.원본 https://blog.naver.com/jword_/223552932222 ↗

[JAVA SPRING] Query method(with JPQL)

[JAVA SPRING] Query method(with JPQL) — #자바스프링 #JAVASPRING #SpringQuery #자바스프링쿼리메서드 #springquerymethod #개발자의...

#Spring#Naver Blog

#자바스프링 #JAVASPRING #SpringQuery #자바스프링쿼리메서드 #springquerymethod #개발자의도구들

🚨📝 📖 📒✏️💡🔍

​

AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다

\* 현재 Spring 프로젝트를 진행 중입니다. 전체적인 목차를 보시려면 여기를 눌러주세요.

이미지

​

복습하고 가요

이전글에서 Criteria API를 통해서 동적으로 쿼리를 생성하여 처리하는 방법에 대해서 다뤘습니다. 지금까지 했던 내용을 간략하게 정리해 볼게요.

​

  • 간단한 CRUD 로직은 JPA의 EnitiMager(Hibernate사용)가 제공한다.
  • 복잡한 로직을 처리하려면 JPQL, Criteria API를 사용하면 된다.
  • 동적으로 쿼리를 생성해야하는 일이 생긴다면, Criteria API를 사용하면 좋다.

​

복잡한 쿼리에 대해서는 확실히 JPQL이나 Criteria API를 사용하면 큰 도움이 됩니다.하지만, 실제 프로젝트에서는 뭐니 뭐니 해도 속도가 생명이잖아요? 그럼 그럴때마다 우리는 Repository에 필요한 로직을 추가하고 구현하는데 시간이 많이 걸리겠죠??

​

JPQL과 Criteria API는 자유도가 높은대신, 원하는 로직이 있으면 하나 하나 생성해서 만들어야하죠. 하지만 자주 쓰이는 로직을 매번 생성하는 것 만큼 번거로운건 없잖아요? 그럼 자주 쓰이는 로직들을 미리 만들어서 그때그때 바로 사용할 수 있으면 참 좋을 것 같은데요. Spring JPA에서 지원하는 Query method가 그런 역할을 하고 있습니다.

오늘 할 것

package com.example.webserver.repository;​import com.example.webserver.dto.ReadingCountCriteria;import com.example.webserver.entity.BoardEntity;​import java.util.List;import java.util.Optional;​public interface BoardRepository {​ List<BoardEntity> findAllByCriteria(); List<BoardEntity> findAll(); Optional<BoardEntity> findById(long id); void removeById(long id); void save(BoardEntity board); List<BoardEntity> findByWriterLike(String writer); List<BoardEntity> findByWriterLikeByCriteria(String writer) ; List<BoardEntity> findByWriterAndNotContainLike(String writer, String notContain); List<BoardEntity> findByWriterAndReadingCount(String writer, String notContain, Integer minReads, Integer maxReads); // List<BoardEntity> findByReadingCountWithCriteria(ReadingCountCriteria readingCountCriteria);}​

그럼 오늘은 이전에 구현해두었던 BoardRespository의 method 들을 Spring Query Method로 구현해봅시다.

Spring JPQ Query Method

Spring에서는 Spring JPA를 지원하고 있씁니다. 일반적인 JPA 명세를 따르면서, Spring 자체적으로 만들어둔 라이브러리로 생각하시면 됩니다.

의존성 설정

build.gradle

이전 프로젝트

implementation 'com.zaxxer:HikariCP:5.0.1'implementation 'org.mariadb.jdbc:mariadb-java-client:2.7.3'​// JPA APIimplementation 'jakarta.persistence:jakarta.persistence-api:3.1.0' // JPA API​// JPA 구현체implementation 'org.hibernate:hibernate-core:6.2.5.Final' // Hibernate Core (JPA 구현체)

Spring JPA 적용 프로젝트

implementation 'org.mariadb.jdbc:mariadb-java-client:2.7.3'​implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

JPA, Hibernate, HikariCP 등 이전 라이브러리를 모두 포함하고 있습니다.

Naming Rule

앞서 Spring JPA는 자주 사용하는 로직에 대해서 직접 구현하지 않고도 API 기능처럼 사용이 가능합니다. 사용하려면 method이름을 지을 때 특정한 규칙을 지켜주시면 됩니다.

\[접두사\] + By + \[속성 이름\] + \[연산자\] + \[추가 조건\]

이런 형태의 이름을 지은 메서드를 생성하면 직접 해당 메서드를 구현하지 않고도 바로 사용할 수 있습니다.

​

💡접두사​조회: find, read, get (보통 find를 사용함)전체 수: count 삭제: delete
List<BoardEntity> findByWriter(String writer);

이런식으로 작성하게되면 writer속성으로 BoardEntity 조회가 가능하게 됩니다.

​

메서드가 워낙 많기 때문에 지원되는 메서드가 궁금하시다면 아래 Spring 공식 사이트에서 확인해보세요1

​

​

https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html

[JPA Query Methods :: Spring Data JPA
Spring Data JPA JPA JPA Query Methods JPA Query Methods This section describes the various ways to create a query with Spring Data JPA. Query Lookup Strategies The JPA module supports defining a query manually as a String or having it being derived from the method name. Derived queries with the pred...
docs.spring.io](https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html)

Repository 구현하기

package com.example.webserver.repository;​import com.example.webserver.dto.ReadingCountCriteria;import com.example.webserver.entity.BoardEntity;​import java.util.List;import java.util.Optional;​public interface BoardRepository {​ List<BoardEntity> findAll(); Optional<BoardEntity> findById(long id); void removeById(long id); void save(BoardEntity board); List<BoardEntity> findByWriterLike(String writer); List<BoardEntity> findByWriterAndNotContainLike(String writer, String notContain); List<BoardEntity> findByWriterAndReadingCount(String writer, String notContain, Integer minReads, Integer maxReads); // List<BoardEntity> findByReadingCountWithCriteria(ReadingCountCriteria readingCountCriteria);}​
package com.example.webserver.repository;​import com.example.webserver.dto.ReadingCountCriteria;import com.example.webserver.entity.BoardEntity;import jakarta.persistence.criteria.CriteriaBuilder;import jakarta.persistence.criteria.CriteriaQuery;import jakarta.persistence.criteria.Predicate;import jakarta.persistence.criteria.Root;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import jakarta.persistence.\*;​import java.util.List;import java.util.Optional;​@Slf4j@RequiredArgsConstructorpublic class BoardRepositoryImpl implements BoardRepository { private final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("onlyJPAUnit"); private final EntityManager entityManager = entityManagerFactory.createEntityManager();​ @Override public List<BoardEntity> findAll() { return entityManager.createQuery("SELECT b FROM BoardEntity b", BoardEntity.class) .getResultList(); }​ // 생략​}

​

이게 기존의 Repository였는데요. 이렇게 interface를 구현 후 Impl클래스에서 상속받아 사용했어야 했습니다. 보시다 싶이 이는 매우 번거로운 일입니다.

​

package com.example.webserver.repository;​import com.example.webserver.entity.BoardEntity;import org.springframework.data.jpa.repository.JpaRepository;​import java.util.List;​public interface BoardRepository extends JpaRepository<BoardEntity, Long> { List<BoardEntity> findByWriterLike(String writer); List<BoardEntity> findByWriterLikeAndWriterNotContaining(String writer, String writerNot); List<BoardEntity> findByWriterLikeAndWriterNotContainingAndReadingCountBetween(String writer, String writerNot , Integer minReads, Integer maxReads);​}​

extends를 통해 JpaRepository를 상속받으면 됩니다. findAll, findById, deleteById, save와 같은 메서드들은 JpaRepository에 구현이 되어 있습니다.

​

추가적으로 구현해야 될 부분은 naming rule에 따라서 구현을 해주면 됩니다.

JPQL 사용하기

Spring Query Method의 가장 큰 단점은 SQL 자유도가 떨어진다는 것입니다. 이를 보완하기 위해 JPQL과 함께 사용할 수 있습니다.

@Query("SELECT b FROM BoardEntity b WHERE b.writer LIKE :writer " + "AND b.writer NOT LIKE :writerNot " + "AND (b.readingCount > :minReads " + "OR b.readingCount <= :maxReads)") List<BoardEntity> findByCustomCriteria( @Param("writer") String writer, @Param("writerNot") String writerNot, @Param("minReads") Integer minReads, @Param("maxReads") Integer maxReads );

이렇게 JPQL이랑 함꼐 사용하면 훨씬 더 많은 로직들을 구현할 수 있습니다.

​