앞글 시나리오에 단어를 추가하고 싶고 지속적인 저장을 하고 싶다는 요구사항을 추가하면 어떻게 될지 고민해 보자.
위의 요구사항을 만족시키려면 데이터를 Database에 저장해야 한다. Database와 연동을 ORM 기반으로 구현할 예정이고 ORM 기반으로 구현하려면 오늘의 주인공 JPA가 필수 이다.
이 블로그에서 특별한 설명이 없으면 Database는 PostgreSQL을 기본으로 사용한다.
JPA(JAVA Persistence API)
이름 그대로 ORM기술에 대한 JAVA 명세다. JPA는 딱 interface 역할만 하고 그 이후의 처리는 Hibernate 같은 구현체를 사용하여 구현한다.
Hibernate
JAVA base로 한 ORM 구현체이다. python에 Sqlalchemy가 있듯이 말이다. 찾아보니 여러가지 ORM 구현체가 있는데 Hibernate가 제일 많이 쓰이는 것 같다. Hiberate는 JDBC layer와 application layer 사이 추상화 층을 추가하여 DB를 더욱 쉽게 다둘 수 있게 만들어 준다.(ORM 기본) 여기를 통해 더 자세한걸 알 수 있다.
PostgreSQL
Enterprise 급 open source Database 이다. 기본적인 기능과 사용법이 Oracle과 비슷하다고 소개 되었다. 필자가 제일 익숙한 DBMS 이기때문에 현재는 특별한 설명이 없으면 모든 글은 PostgreSQL을 사용한다. 좀 더 자세히 파악하고 싶다면 이글을 추천 드린다
JPA 적용
JPA을 적용에 앞서 PostgreSQL 서비스를 올려야 한다. Docker 기반으로 PostgreSQL 서비스를 하는법은 여기를 참조하시기 바랍니다.
Gradle 설정
JPA을 적용하려면 앞의 글에서의 설정 정보를 그대로 사용하면 컴파일 에러가 발생한다. dependencies에 jpa에 대한 종속성을 추가 한다.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.5.6"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.5.31"
kotlin("plugin.spring") version "1.5.31"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("org.postgresql:postgresql")
implementation("org.hibernate.validator:hibernate-validator:6.2.0.Final")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
Dababase Entity 정의
ORM를 사용하여 개발하려면 먼저 Entity부터 정의해야 한다. 위 시나리오대로라면 하나의 Entity면 충분할것 같다. 저번 글에서 프로젝트 구조를 어느 정도는 잡아 놓았다. 지금부터 Entity을 프로젝트에 model 폴더를 만들고 그 밑에 넣어보자.
/src/main/kotlin/com.example.demo/Model/word.kt
@Entity
class Word {
@Id
@GeneratedValue(generator = "word_generate")
var id: Long? = null
var name: String? =null
}
key가 아이디이고 name이 단어인 테이블을 정의 했다.
JPA 사용
앞글에서 작성했던 WordRepostitory를 JPA 기반으로 바꾸자. JPA로부터 상속받아서 Interface만 정의하면 기본적인 CRUD는 Hibernate에서 구현해준다.
/src/main/kotlin/com.example.demo/Repository/WordRepository.kt
interface WordRepository: JpaRepository<Word,Long>{
fun findByName(name:String):List<Word>
}
Repository 수정과 함께 Service 쪽도 수정이 필요하다. 서비스에서는 위에서 생성한 WordRepository 함수를 이용하여 단어를 검색해야 한다.
/src/main/kotlin/com.example.demo/Service/SearchService.kt
@Service
class SearchService(
private val wordRepository: WordRepository
) {
fun search_word(word: String):List<Word>{
return wordRepository.findByName(word)
}
}
여기서 잠깐 앞에 글과 비교했을경우 앞의 경우는 로직으로 개발자가 구현하는 반면에 여기서는 JPA과 Hibernate를 이용하여 좀 더 쉽게 구현 할 수 있다.(필드로 검색 기능은 기본적으로 Interface만 정의하면 자동 구현 된다.)
마지막으로 Controller를 아래와 같이 변경한다.
/src/main/kotlin/com.example.demo/Repository/WordRepository.kt
@RestController
class SearchController(
@Autowired var searchService: SearchService,
){
@GetMapping("search")
fun search_word(@RequestParam word: String): List<Word>{
return searchService.search_word(word)
}
}
@RestController
class WordController(
@Autowired var wordRepository: WordRepository
)
{
@PostMapping("words")
fun save_word(@RequestBody word: Word): Word{
return wordRepository.save(word)
}
}
여기서는 직관적으로 코드를 읽기 위하여 Controller class를 두개를 정의하였다.
먼저 두번째 WordController class부터 보면 post 방식으로 새로운 단어를 받는 것을 확인 할 수 있다.
본 글의 요구사항을 만족시키는 부분이다. 또한 검색과 단어를 두개의 Rest API로 구분하여 사용했다.
모든 준비는 끝났다. 마지막으로 resources 폴더 아래 application.properties에 아래처럼 DB 접속 정보를 넣는다.
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:postgresql://localhost:5432/word
spring.datasource.username = Test
spring.datasource.password = Test1234
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
## Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
Ref:
https://jsonobject.tistory.com/462
Spring Boot, Spring Data JPA, Querydsl로 타입 세이프 쿼리 작성하기
개요 Java 진영에서 RDBMS에 접근하는 방법은 기존의 MyBatis가 지배하던 시대에서 JPA로 빠르게 이동하고 있다. 특히, Spring Data JPA 의 등장으로 불필요한 코드가 상당히 줄어들면서 새로 시작하는 프
jsonobject.tistory.com
https://www.javaguides.net/2018/11/hibernate-framework-overview-architecture-bascis.html
Hibernate Framework Overview - Architecture and Basics
Before getting started with Hibernate framework familiar with a few basic concepts of the hibernate framework, it's architecture, it's benifits, advantages over JDBC etx.
www.javaguides.net
https://d2.naver.com/helloworld/227936
'Spring boot' 카테고리의 다른 글
[Spring] Spring AOP 개념 및 구현 (0) | 2021.11.15 |
---|---|
[Spring] Kotlin + Spring boot 에 MongoDB을 도입(Spring Data MongoDB, Querydsl) (0) | 2021.11.14 |
[Spring boot] DI 설정에 관하여 (0) | 2021.11.12 |
[Spring boot]Dependency injection(DI) (0) | 2021.11.11 |
[Spring] Kotlin + Spring Boot Controller 에 대하여 (0) | 2021.11.04 |
Comment