Skip to main content

Command Palette

Search for a command to run...

[ 살펴보기 ] Spring Data JPA - Query methods

Updated
5 min readView as Markdown
[ 살펴보기 ] Spring Data JPA - Query methods
C

A developer living in Busan, Korea

Table data 조작을 위해 entity의 repository를 구성할 때 CrudRepository, ListCrudRepository, JpaRepository를 extends하여 crud 작업에 필요한 기본적인 method를 상속받아 사용할 수 있다.

UserRepository.java

...
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

위의 예제에서 UserRepository가 extends하는 JpaRepository interface는 다음과 같다.

JpaRepository.class

...

@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    void flush();

    <S extends T> S saveAndFlush(S entity);

    <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

    /** @deprecated */
    @Deprecated
    default void deleteInBatch(Iterable<T> entities) {
        this.deleteAllInBatch(entities);
    }

    void deleteAllInBatch(Iterable<T> entities);

    void deleteAllByIdInBatch(Iterable<ID> ids);

    void deleteAllInBatch();

    /** @deprecated */
    @Deprecated
    T getOne(ID id);

    /** @deprecated */
    @Deprecated
    T getById(ID id);

    T getReferenceById(ID id);

    <S extends T> List<S> findAll(Example<S> example);

    <S extends T> List<S> findAll(Example<S> example, Sort sort);
}

위의 예제에서 볼 수 있듯이 JpaRepositoryListCrudRepository, ListPagingAndSortingRepository, QueryByExampleExecutor interface를 extends하고 있다. 그리고 ListCrudRepository interface는 CrudRepository interface를 extends하고 있으므로 JpaRepository interface를 extends하는 repository는 ListCrudRepository interface와 CrudRepository interface가 구현하는 method를 사용할 수 있다.

ListCrudRepository.class

...

@NoRepositoryBean
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
    <S extends T> List<S> saveAll(Iterable<S> entities);

    List<T> findAll();

    List<T> findAllById(Iterable<ID> ids);
}

또한 예제와 같이 특정 repository에 대한 interface를 구성하고 해당 interface를 실제로 구현하는 class를 별로도 작성하지 않아도 spring data jpa가 application을 실행할 때 interface에 대한 구현 class를 자동으로 생성해준다.

Custom Query

CrudRepository interface에 findById method가 정의되어 있기에 JpaRepository를 extends하는 repository는 findById method를 사용할 수 있지만 만약 entity의 다른 field를 통해 search를 하고 싶다면 Repository interface에 명시적으로 추가해주어야 한다. 아래는 email field를 기준으로 select query를 수행하는 method를 추가하는 예제다.

User.java

...

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
@Table(name="users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name="name", length = 30)
    private String name;

    @Column(name = "email", columnDefinition="VARCHAR")
    private String email;

}

UserRepository.java

...

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByEmail(String email);
}

위의 custom method를 추가할 때는 예제와 같이 findBy와 같은 query method subject와 entity field name 조합의 형태로 추가한다. 위와 같이 Spring Data가 지원하는 query method keyword를 사용하여 형식에 맞게 repository interface에 method를 추가하면 실제 구현 class와 query는 내부적으로 자동으로 생성해준다.

Query method subject keywords

위의 예제와 같이 custom method를 추가할 때 사용할 수 있는 keyword 중 일부를 살펴보자. 사용할 수 있는 keyword의 자세한 목록은 documentation을 통해 확인할 수 있다. ( Reference - Supported query method subject keywords )

  • findBy… : Entity의 field를 기준으로 데이터를 select하는 method를 추가하기 위해 사용할 수 있다. 만약 entity의username이라는 field를 기준으로 select query를 수행하는 method를 추가하고 싶다면 아래와 같이 추가한다.

    User.java

      ...
    
      @Getter
      @Setter
      @AllArgsConstructor
      @NoArgsConstructor
      @ToString
      @Entity
      @Table(name="users")
      public class User {
    
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
    
          private String username;
    
          private String email;
      }
    

    UserRepository.java

      ...
    
      @Repository
      public interface UserRepository extends JpaRepository<User, Long> {
          List<User> findByUsername(String email);
      }
    
  • existsBy… : Entity의 특정 field를 기준으로 select를 수행하고 데이터 존재 여부를 boolean 형태로 return하는 method를 추가하기 위해 사용할 수 있다.

    UserRepository.java

      ...
    
      @Repository
      public interface UserRepository extends JpaRepository<User, Long> {
          boolean existsByEmail(String email);
      }
    

    UserService.java

      ...
    
      @Service
      public class UserService {
    
          private final UserRepository userRepository;
    
          @Autowired
          public UserService(UserRepository userRepository, UserMapper userMapper) {
              this.userRepository = userRepository;
          }
    
          public boolean isEmailFound(String email) {
              return userRepository.existsByEmail(email);
          }
    
      }
    
  • deleteBy… : Eentity의 특정 field를 기준으로 데이터를 삭제하는 method를 추가할 때 사용할 수 있다. 만약 entity의 username이라는 field를 기준으로 delete query를 수행하는 method를 추가하고 싶다면 아래와 같이 추가한다.

    UserRepository.java

      ...
    
      @Repository
      public interface UserRepository extends JpaRepository<User, Long> {
          void deleteByEmail(String email);
      }
    

    UserService.java

      ...
    
          @Transactional
          public void deleteByEmail(String email) {
              userRepository.deleteByEmail(email);
          }
    
      ...
    

Reserved methods

반면 다음과 같이 id property 기반 method들은 CrudRepository interface에서 기본적으로 제공하는 method이므로 별도로 추가할 필요가 없다.

  • findById

  • findAllById

  • existsById

  • deleteById

여기서 주의할 점은 entity를 구성할 때 @Id decorator를 어느 property에 설정했느냐에 따라 위의 reserved method의 동작이 달라질 수 있다.

예를 들어 User entity가 다음과 같이 구성되어 있다고 가정해보자.

...
@Entity
@Table(name="users")
public class User {

    private Long id;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer pk;

    @Column(name="name", length = 30)
    private String name;

    @Column(name = "email", columnDefinition="VARCHAR")
    private String email;

...

위의 예제에선 User class의 pk filed에 @Id annotation을 추가하여 entity의 id field 역할을 하고 있다. 그러므로 CrudRepository interface가 기본적으로 제공하는 method가 이름은 findById 또는 deleteById와 같이 뒤에 id가 붙지만 위의 예제에서 reserved method를 통해 조회하고 삭제하는 기준이 되는 field는 id field가 아니라 pk field다.

즉, 위의 Entity를 기준으로 repository를 구성하고 findById method에 1을 parameter로 전달하여 실행하면 id column이 1인 data가 아닌 pk column이 1인 데이터가 조회된다.

Query method predicate keywords

Spring Data JPA에서 지원하는 predicate keyword를 통해 and, or와 같은 연산을 함께하는 method를 repository에 선언할 수 있다. 사용할 수 있는 keyword의 자세한 목록은 documentation을 통해 확인할 수 있다. ( Reference - Supported query method predicate keywords and modifiers )

  • And : method에 And predicate keyword를 조합해 and 연산을 하는 method를 추가할 수 있다. 다음은 parameter로 전달 받은 email과 name을 and 연산을 통해 둘 다 일치하는 data를 조회하는 method를 추가하는 예제다.

    UserRepository.java

      @Repository
      public interface UserRepository extends JpaRepository<User, Long> {
          User findByEmailAndName(String email, String name);
      }
    

    UserService.java

      ...
      @Service
      public class UserService {
    
          private final UserRepository userRepository;
    
          @Autowired
          public UserService(UserRepository userRepository, UserMapper userMapper) {
              this.userRepository = userRepository;
          }
    
          public User findByEmailAndName(String email, String name) {
              return userRepository.findByEmailAndName(email, name);
          }
    
      }
    
  • OR : method에 OR predicate keyword를 조합해 or 연산을 하는 method를 추가할 수 있다. 다음은 parameter로 전달 받은 email과 name을 or 연산을 통해 둘 중 하나라도 일치하는 data를 조회하는 method를 추가하는 예제다.

    UserRepository.java

      ...
      @Repository
      public interface UserRepository extends JpaRepository<User, Long> {
          List<User> findByEmailOrName(String email, String name);
      }
    

    UserService.java

      ...
    
      @Service
      public class UserService {
    
          private final UserRepository userRepository;
    
          @Autowired
          public UserService(UserRepository userRepository, UserMapper userMapper) {
              this.userRepository = userRepository;
          }
    
          public List<User> findByEmailOrName(String email, String name) {
              return userRepository.findByEmailOrName(email, name);
          }
    
      }
    

More from this blog

[ 살펴보기 ] TypeORM - Transactions, Migration

Transation Database 종류에 따라 detail한 부분은 차이점이 조금씩 있겠지만 각 sql statement는 개별적인 transaction block을 통해 실행되며 Database 설정에 따라 sql statement의 실행 결과가 자동으로 commit되어 영구히 적용되거나 commit을 직접 실행하기 전까지는 영구히 적용되지 않을 수 있다. 대부분의 경우 default로 sql statement 실행 결과가 자동으로 comm...

Feb 9, 20256 min read
[ 살펴보기 ] TypeORM - Transactions, Migration

Dev Diary

184 posts

[ 살펴보기 ] Spring Data JPA - Query methods