Data JPA는 CRUD 동작과 많은 기능을 지원합니다.
Data JPA는 entity가 아닌 DTO에 SQL 을 사용해 query를 지원하지 않습니다.
JPA repository에 상속시켜서 custom methods를 추가할 수 있습니다.
다음 조건을 만족하는 search function을 구현해볼 것입니다.
case 구별x, title or descipriotn이 term을 contains 하는 entries 를 return
title에 대해 ascending order로 sort
JDBC를 사용해 호출되는 SQL query 를 사용해야 합니다.
DTO 객체의 list를 return합니다.
사용자 정의 저장소 인터페이스를 만든 후에는 자연스럽게 구현해야 합니다. JDBC를 사용하여 검색 결과를 가져오는 저장소 클래스를 만들 수 있는 방법에 대해 알아보겠습니다.
custom method를 interface에 선언하려면 method가 DTO list를 return 해야하므로 DTO를 구현해야합니다.
search result page가 id와 title fields만 값으로 사용하기 때문에 DTO는 2 필드만 가집니다.
``
public final class TodoSearchResultDTO {
private Long id;
private String title;
public TodoSearchResultDTO() {}
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setId(Long id) {
this.id = id;
}
public void setTitle(String title) {
this.title = title;
}
}
``
``
interface CustomTodoRepository {
List<TodoSearchResultDTO> findBySearchTerm(String searchTerm);
}
``
명명된 매개 변수를 사용하는 SQL 쿼리를 만들고 JDBC를 사용하여 해당 쿼리를 호출하려면 사용자 지정 저장소 인터페이스를 구현하기 전에 NamedParameterJdbcTemplatebean을 구성해야 합니다. 예제 응용 프로그램의 지속성 계층을 구성하는 응용 프로그램 컨텍스트 구성 클래스를 다음과 같이 변경하여 이 빈을 구성할 수 있습니다:
jdbcTemplate() 메서드를 구성 클래스에 추가하고 @Bean 주석을 사용하여 주석을 추가합니다. 이 메서드는 DataSource 개체를 메서드 매개 변수로 사용하고 NamedParameterJdbcTemplate 개체를 반환합니다.
새 NamedParameterJdbcTemplate 개체를 생성하고 생성된 개체를 반환하여 메서드를 구현합니다.
PersistenceContext 클래스의 관련 부분은 다음과 같습니다:
``
@Bean
NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
``
사용자 지정을 구현하는 사용자 지정 리포지토리 클래스 만들기리포지토리 인터페이스를 수행합니다. 기본적으로 사용자 지정 리포지토리 클래스의 이름은 저장소 인터페이스의 이름Imple 구문을 따라야 합니다. 리포지토리 인터페이스의 이름이 TodoRepository이므로 사용자 지정 리포지토리 클래스의 이름은 TodoRepositoryImp이어야 합니다.
생성된 클래스에 @Repository 주석을 추가합니다.
3.제목 또는 설명에 지정된 검색어가 포함된 작업관리 항목의 ID와 제목을 반환하고 제목 열의 값을 사용하여 오름차순으로 쿼리 결과를 정렬하는 SQL 조회를 작성합니다. 이 SQL 쿼리를 정적 최종 필드의 값으로 설정합니다.
최종 NamedParameterJdbcTemplate 필드를 리포지토리 클래스에 추가하고 생성자 주입을 사용하여 이 필드의 값을 주입합니다.
다음 단계에 따라 findBySearchTerm() 메서드를 구현합니다:
@Transactional 주석을 사용하여 메서드에 주석을 달고 트랜잭션을 읽기 전용으로 표시합니다. 이렇게 하면 SQL 쿼리가 항상 읽기 전용 트랜잭션 내에서 호출됩니다.
SQL 쿼리의 쿼리 매개 변수를 포함하는 Map 개체를 만들고 메소드 매개 변수로 제공된 검색 용어를 생성된 맵에 넣습니다.
BeanPropertyRowMapper 클래스를 사용하여 SQL 쿼리를 호출하고 쿼리 결과를 TodoSearchResultDTO 개체 목록으로 변환합니다. 선택한 열의 별칭이 "대상 클래스"의 속성 이름과 일치하는 한 이 방법을 사용할 수 있습니다.
쿼리 결과를 반환합니다.
``
@Repository
final class TodoRepositoryImpl implements CustomTodoRepository {
private static final String SEARCH_TODO_ENTRIES = "SELECT id, title FROM todos t WHERE " +
"LOWER(t.title) LIKE LOWER(CONCAT('%',:searchTerm, '%')) OR " +
"LOWER(t.description) LIKE LOWER(CONCAT('%',:searchTerm, '%')) " +
"ORDER BY t.title ASC";
private final NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
TodoRepositoryImpl(NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Transactional(readOnly = true)
@Override
public List<TodoSearchResultDTO> findBySearchTerm(String searchTerm) {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("searchTerm", searchTerm);
List<TodoSearchResultDTO> searchResults = jdbcTemplate.query(SEARCH_TODO_ENTRIES,
queryParams,
new BeanPropertyRowMapper<>(TodoSearchResultDTO.class)
);
return searchResults;
}
}
``
사용자 지정 메서드를 선언하는 사용자 지정 리포지토리 인터페이스를 확장하여 사용자 지정 메서드를 리포지토리 인터페이스에 추가할 수 있습니다. 즉, Todo Repository 인터페이스를 수정하여 Custom을 확장해야 합니다리포지토리 인터페이스를 수행합니다.
TodoRepository 인터페이스를 수정한 후 소스 코드는 다음과 같습니다:
``
interface TodoRepository extends Repository<Todo, Long>, CustomTodoRepository {
void delete(Todo deleted);
List<Todo> findAll();
Optional<Todo> findOne(Long id);
void flush();
Todo save(Todo persisted);
}
``
요약
SQL을 써서 DTO에 query를 하려면, JPA repositroy에 custom method를 추가한 custom repository를 상속시킵니다.
BeanPropertyRowMapper class 를 써서 query results 를 objects 로 매핑(변환?)할 수 있습니다.