기본 CRUD 동작을 지원하는 Repository를 생성하는 것만으로 실제 앱을 구현하기 어렵다. custom한 검색 기준을 써서 db query를 할 수 있어야한다.
Query methods를 사용하여 custom db queries를 생성할 수 있다.
Todo Object를 특정 Id값을 갖는 객체를 조회하는 query를 생성하고 싶다면
TodoRepository<>에 findById() 라는 query method를 추가한다.
하나의 값, 혹은 하나 이상(리스트) 를 반환할 수 있다.
비동기적으로 호출되는 query method를 생성할 수도 있다.
상황에 따라 다른 반환 값을 반환하도록 할 수 있다.
Basic type 으로 지정하면 basic type or null 반환
Entity 으로 지정하면 entity의 type or null 반환
Optional<> - 해당 객체를 담는 Optional 혹은 empty Optional 객체를 반환
특정 컬럼을 조회할 때는 basic type or Optional<>을 반환하네
전체 entity 컬럼을 조회할 때는 entity type or Optional을 하겠네
예제)
``
interface TodoRepository extends Repository<Todo, Long> {
@Query("SELECT t.title FROM Todo t where t.id = :id")
String findTitleById(@Param("id") Long id);
@Query("SELECT t.title FROM Todo t where t.id = :id")
Optional<String> findTitleById(@Param("id") Long id);
Todo findById(Long id);
Optional<Todo> findById(Long id);
}
``
List<> - query method가 query 결과들을 포함하는 List 혹은 empty List를 반환한다.
Stream<> - query method가 query 결과들을 access하는데 쓰이는 Stream 혹은 empty Stream을 반환한다.
예)
``
interface TodoRepository extends Repository<Todo, Long> {
List<Todo> findByTitle(String title);
Stream<Todo> findByTitle(String title);
}
``
@Async를 붙이고 2. 반환 값을 Future<> 로 지정한다.
예)
``
interface TodoRepository extends Repository<Todo, Long> {
@Async
@Query("SELECT t.title FROM Todo t where t.id = :id")
Future<String> findTitleById(@Param("id") Long id);
@Async
@Query("SELECT t.title FROM Todo t where t.id = :id")
Future<Optional<String>> findTitleById(@Param("id") Long id);
@Async
Future<Todo> findById(Long id);
@Async
Future<Optional<Todo>> findById(Long id);
@Async
Future<List<Todo>> findByTitle(String title);
@Async
Future<Stream<Todo>> findByTitle(String title);
}
``
method parameter의 순서가 어느 placeholders가 parameters로 대체될지 결정한다.
1번째 method parameter가 1번째 placeholder를 대체하고,
2번째 method parameter가 2번째 placeholder를 대체한다.
position based parameter binding 예제
``
interface TodoRepository extends Repository<Todo, Long> {
public Optional<Todo> findByTitleAndDescription(String title, String description);
@Query("SELECT t FROM Todo t where t.title = ?1 AND t.description = ?2")
public Optional<Todo> findByTitleAndDescription(String title, String description);
@Query(value = "SELECT * FROM todos t where t.title = ?0 AND t.description = ?1",
nativeQuery=true
)
public Optional<Todo> findByTitleAndDescription(String title, String description);
}
``
이 방식은 조금 실수를 하기 쉽다.
db query를 breaking하지 않고서는
method parameters의 순서 또는 placeholders의 순서를 바꿀수 없기 떄문이다.
전 방식의 numeric placeholders를 db query의 parameter의 이름으로 대체한다.
method parameters에 각각 @Param 을 붙인다.
method parameter 값으로 대체될 parameter의 name을 지정한다.
query method에 parameter를 (@Param("id") Long id) 이렇게 지정한다.
@Param에 query method에 전달될 parameter의 이름을 지정하고,
값 전달받을 method parameter를 지정한다.
``
interface TodoRepository extends Repository<Todo, Long> {
@Query("SELECT t FROM Todo t where t.title = :title AND t.description = :description")
public Optional<Todo> findByTitleAndDescription(@Param("title") String title,
@Param("description") String description);
@Query(
value = "SELECT * FROM todos t where t.title = :title AND t.description = :description",
nativeQuery=true
)
public Optional<Todo> findByTitleAndDescription(@Param("title") String title,
@Param("description") String description);
}
``