시작하며
이전 글에서 우리는 DB를 만들고 커넥션을 설정한 다음, 테이블을 만드는 작업을 했다.
- 코틀린 스프링 프로젝트에 Spring Data JPA 와 H2 Database 사용 설정하기
- [Kotlin Spring JPA] @Entity, @Table, @Id, @Column 사용해 Table 정보 설정하는 방법 한 번에 정리하기
이제 테이블을 만들었으니, 이번 글에서는 쿼리를 만들어 실행해보자.
일반적으로 쿼리를 실행하기 위해서는 쿼리문을 작성해야 하지만, Spring Data JPA를 사용하면 기본적인 쿼리문을 모두 기본으로 생성되게 만들 수 있는데 이런 역할을 하는 것이 바로 JpaRepository이다. 지금부터 JpaRepository를 사용해 쿼리를 만들어 실행해보자.
JpaRepository 상속 받는 객체 만들기
JpaRepository를 사용하는 방법은 간단하다. 예를 들어 다음과 같은 Vocabulary 엔티티가 있다고 해보자.
@Entity
data class Vocabulary(
@Id
val id: String = UUID.randomUUID().toString(),
val word: String,
val meaning: String
)
그러면 이 JpaRepository<Vocabulary, String> 을 상속하는 VocabularyRepository 인터페이스를 다음과 같이 만들면 이 VocabularyRepository는 자동으로 Bean으로 등록된다.
interface VocabularyRepository: JpaRepository<Vocabulary, String>
여기서 JpaRepository는 JpaRepository<t, id=""> 라는 제네릭 타입을 갖는데, ID는 바로 Vocabulary를 구분할 수 있는 단일 ID 값이 되며 일반적으로 Primary Key로 등록되는 타입을 사용하면 된다.
이제 JpaRepository가 만들어졌으니 이걸 사용해 데이터 베이스에 쿼리를 실행하는 컨트롤러를 만들어보자.
JpaRepository 사용해 쿼리 실행하기
JpaRepository를 사용해 쿼리를 실행하는 방법은 간단하다. JpaRepository를 상속 받은 인터페이스인 VocabularyRepository가 Bean으로 등록되었으므로, 이를 사용해 쿼리를 실행하면 된다.
이때 JpaRepository에는 CRUD를 위한 findById, findAll, save, deleteById 등의 메서드가 이미 정의되어 있기 때문에 다음과 같이 직접 쿼리를 작성할 필요 없이 코틀린 코드만 작성하면 되며, 그러면 원하는 쿼리가 실행된다.
@RestController
class VocabularyController(
private val vocabularyRepository: VocabularyRepository
) {
@GetMapping("/vocabulary")
fun getVocabularyById(@RequestParam id: String): Vocabulary {
return vocabularyRepository.findById(id).get()
}
@GetMapping("/vocabulary/all")
fun getAllVocabulary(): List<Vocabulary> {
return vocabularyRepository.findAll()
}
@PostMapping("/vocabulary")
fun addVocabulary(
@RequestParam word: String,
@RequestParam meaning: String
): Vocabulary {
val vocabulary = Vocabulary(word = word, meaning = meaning)
return vocabularyRepository.save(vocabulary)
}
@DeleteMapping
fun deleteVocabulary(@RequestParam id: String) {
vocabularyRepository.deleteById(id)
}
}
위의 코드의 findById 함수를 보면 인자로 id를 넘기고 있는데, 이 Id는 Vocabulary 객체에서 @Id 어노테이션이 붙은 필드인 id에 대응된다.
이처럼 쿼리를 직접 작성하지 않고도 손쉽게 쿼리를 실행할 수 있다.
@Query 사용해 커스텀 쿼리 만들기
JpaRepository는 기본적인 CRUD 쿼리만을 정의하고 있기 때문에 종종 JpaRepository에 정의된 쿼리 함수들 만으로 충분하지 않은 경우가 있다. 예를 들어 word를 사용해 검색 시스템을 만드는 경우를 생각해보자. 이런 경우는 검색을 위해 LIKE 문을 사용해야 하는데, JpaRepository에는 이런 쿼리 함수가 정의되어 있지 않다.
이런 경우에는 JpaRepository를 상속 받는 Repository 안에 @Query 어노테이션을 사용해 커스텀 쿼리를 직접 작성하면 된다. 만약 인자를 커스텀 쿼리 내부로 넘겨야 한다면 @Query에는 %:[인자명]% 을 적고, 함수의 인자에는 @Param("[인자명]") 과 함께 인자를 넘기면 된다.
예를 들어 word를 사용해 검색을 하는 함수는 다음과 같이 만들 수 있다.
interface VocabularyRepository: JpaRepository<Vocabulary, String> {
@Query("SELECT v FROM Vocabulary v WHERE v.word LIKE %:word%")
fun searchByWord(@Param("word") word: String): List<Vocabulary>
}
이곳에서는 @Param("word") word: String 를 받아 이를 LIKE 문 뒤에 %:word% 형태로 넣고 있다.
추가 자료
JpaRepository는 어떻게 Bean으로 등록되는가?
JpaRepository 내부를 보면 @NoRepositoryBean만 붙어 있을 뿐 @Component 어노테이션이 붙어있지 않다. @NoRepositoryBean은 레포지토리를 표시하는 메서드를 정의하는 인터페이스라는 것을 명시하고 해당 인터페이스를 실제 Bean객체로 등록되지 않게 하는 역할을 하기 때문에 Bean으로 등록되지 않는다.
@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>
이를 처리하는 것은 @EnableJpaRepositories 이라는 어노테이션으로, @EnableJpaRepositories를 사용하면 특정 패키지 하위의 JpaRepository를 스캔해 Bean으로 별도로 등록해준다. 예를 들어 com.somepackage 하위의 모든 JpaRepository를 등록하려면 다음과 같이 작성 후 이를 애플리케이션 클래스 위에 붙여 주면 된다.
@EnableJpaRepositories(value = ["com.somepackage"])
다만 이것은 스프링 부트를 사용하면 @SpringBootApplication이 붙어 있는 패키지와 하위 패키지에 대해 생략이 가능하다고 한다.
전체 코드: GitHub
이 프로젝트가 도움이 되셨다면 저장소에 Star⭐️를 눌러주세요! Stargazers는 다음 페이지에서 확인할 수 있습니다.