본문 바로가기
개발 이야기/Springboot

[Kotlin] Springboot에서 MySQL + JPA 사용법

by 농개 2020. 2. 23.
반응형

지난번 Springboot + gradle 설정(https://basketdeveloper.tistory.com/73)에 이어

Spring boot에서 DB(Mysql) 간단 연동법과 JPA를 사용하는 방법을 정리해봅니다.

 

 

 

 

 

 

 

01. build.gradle.kt 에 Dependency 추가

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.springframework.boot.gradle.tasks.run.BootRun

plugins {
	id("org.springframework.boot") version "2.2.4.RELEASE"
	id("io.spring.dependency-management") version "1.0.9.RELEASE"
	kotlin("jvm") version "1.3.61" 
	kotlin("plugin.spring") version "1.3.61" 
	kotlin("plugin.jpa") version "1.3.61" // <-- 추가됨
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
	mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("org.springframework.boot:spring-boot-starter-data-jpa") // <-- 추가됨
	implementation("org.springframework.boot:spring-boot-starter-jdbc")     // <-- 추가됨

	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

	implementation("mysql:mysql-connector-java")		// <-- 추가됨

	testImplementation("org.springframework.boot:spring-boot-starter-test") {
		exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
	}

}

tasks.withType<Test> {
	useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
	kotlinOptions {
		freeCompilerArgs = listOf("-Xjsr305=strict")
		jvmTarget = "1.8"
	}
}

tasks.getByName<BootRun>("bootRun") {
	main = "com.example.demo.DemoApplicationKt"
}

위 설정은 Spring boot Initializer를 통해 생성한 build.gradle.kt 에서 몇가지 dependency를 추가한 것입니다.

  • JPA사용을 위한 Kotlin 플러그인을 추가해줬습니다. (plugin.jpa)
  • 그리고 아래 라이브러리를 추가해줍시다.
    • org.springframework.boot:spring-boot-starter-data-jpa
    • org.springframework.boot:spring-boot-starter-jdbc
    • mysql:mysql-connector-java

 

02. application.properties 수정

server.address=localhost
server.port=8080

spring.datasource.firstdb.jdbc-url=jdbc:mysql://[DB호스트]:[DB포트]/[DB명]
spring.datasource.firstdb.username=사용자명
spring.datasource.firstdb.password=패스워드
spring.datasource.firstdb.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database=mysql
spring.jpa.show-sql=true

 

위와 같이 작성해주었습니다.

DB 정보들은 각자 환경에 맞게 변경해야합니다.

firstdb는 임의로 정한것이며 이것도 각자 datasource 이름을 명명하면 됩니다.

 

 

 

03. Config 파일 작성

package com.example.demo.spring

import com.zaxxer.hikari.HikariDataSource
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.orm.jpa.JpaTransactionManager
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.annotation.EnableTransactionManagement
import javax.persistence.EntityManagerFactory
import javax.sql.DataSource

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactory",
        transactionManagerRef = "transactionManager",
        basePackages = ["com.example.demo.api"]
)
class DataSourceConfig {
    @Primary
    @Bean
    @ConfigurationProperties("spring.datasource.firstdb")
    fun dataSource(): DataSource {
        val dataSource = DataSourceBuilder.create().type(HikariDataSource::class.java).build()

        // UTF-8
        dataSource.connectionInitSql = "SET NAMES utf8mb4"

        return dataSource
    }

    @Primary
    @Bean
    fun entityManagerFactory(
            builder: EntityManagerFactoryBuilder): LocalContainerEntityManagerFactoryBean {
        return builder
                .dataSource(this.dataSource())
                .packages("com.example.demo.api")
                .persistenceUnit("firstdb")
                .build()
    }

    @Primary
    @Bean
    fun transactionManager(builder: EntityManagerFactoryBuilder): JpaTransactionManager {
        return JpaTransactionManager(entityManagerFactory(builder).`object`!!)
    }
}

 

Annotation의 강력한 기능을 이용해 코틀린소스로 설정을 하였습니다.

basePackage나 ConfigurationProperties는 각자에 맞게 수정해주시면 되구유. 간단히 각 함수를 설명하자면...

  • dataSource : application.properies에서 DB 연결을 위한 설정값으로 dataSource를 만듭니다. 한글입력처리를 위해서 SET NAMES utf8이 추가됬구요. (위 설정은 여러 DB를 연결할때 설정입니다. 하나만 사용한다면 굳이 dataSource 만들필요없이 springboot 설정파일만 건드려도 됩니다...)
  • entityManager : JPA는 Entity라는 개념이 있습니다. 그외에도 Context와 Persist 등 생소한것들이 있는데.. 일단 넘어갑니다. (설명하기엔 저도 아직 부족하네요 ㅜㅜ)
  • transactionManager : 트랜잭션을 위한 설정입니다. 해당 글에선 사용하지 않겠지만, 필수적인 개념이죠.

 

04. 간단히 사용해보기

4-1. 패키지 구성

member라는 패키지를 만들고 domain, repository를 만들어줬습니다. 그리고 MemberController를 생성했습니다.

 

4-2. Domain 작성

package com.example.demo.api.member.domain

import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Table

@Entity
@Table(name = "MEMBER")
data class Member(
        @Id
        @Column(name = "id")
        var id: String = "",

        @Column(name = "name")
        var name: String = "",
        @Column(name = "type")
        var type: String = "",
        @Column(name = "password")
        var password: String = "",
        @Column(name = "email")
        var email: String = ""
)

위와 같이 작성해봤어요.

각자 테이블 구조에 맞게 맞춰주면됩니다.

 

여기서 주의할점!!

@Table의 name을 위처럼 대문자로 사용하기 위해선 아래와 같은 설정이 application.properties에 추가되야합니다.

...(생략)
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

 

 

 

 

 

 

 

4-3. Repository 작성

package com.example.demo.api.member.repository

import com.example.demo.api.member.domain.Member
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface MemberRepository : JpaRepository<Member, String>

위와같이 Repository로 작성해줍니다.

JpaRepository로 부터 인터페이스 상속 받았는데요. 요놈은 내부적으로 CrudRepository를 상속받아 구현되 있습니다.

Paging등 더 기능이 강력합니다.

위처럼 interface만 정의해줘도 find, save와 같은 메서드를 사용하여 DB를 조회하고 데이터를 입력할 수 있습니다.

 

만약 Primary Key가 숫자라면(ex. auto increament와 같은 설정사용시)

interface memberRepository: JpaRepository<Member, Long>과 같이 변경해줘야합니다.

 

 

 

4-4. Controller 작성

package com.example.demo.api.member

import com.example.demo.api.member.domain.Member
import com.example.demo.api.member.repository.MemberRepository
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/members")
class MemberController(
        val memberRepository: MemberRepository
) {
    @GetMapping()
    fun getMembers(): ResponseEntity<*> {
        val users = memberRepository.findAll()

        return ResponseEntity.ok(users)
    }

    @PostMapping()
    fun setMember(@RequestBody member: Member): ResponseEntity<*> {
        val res = memberRepository.save(member)

        return ResponseEntity.ok(res)
    }

}

간단히 Member 테이블의 모든 row를 가져오는 것과 Member 테이블에 row를 하나 입력하는 Endpoint를 만들어 줬습니다.

@RestController로 작성되었기 때문에 이 예제는 API 서버의 역할을 하는 걸로 가정합니다.

 

 

05. 실행

서버를 띄워 확인해봅시다.

정상 작동하네요. 하지만 빈 배열이 응답합니다.

 

그렇다면 POST로 member를 입력 후, 다시 조회해봅시다.

 

API 툴을 사용해서 아래와 같은 JSON 데이터를 POST 전송했습니다.

{
  "id": "test2",
  "name":"rrrrr",
  "password":"wfwef",
  "type":"2",
  "email":"test@eimeeme.com"
}

 

그리고 다시 조회 API를 호출해보면

정상동작을 확인 할 수 있습니다.

 

 

 

 

 

 

반응형