little star's memory

競プロ、なぞなぞ

Kotlinでサーバーサイドのお勉強 第4回 データベース

引き続きMicronautを使っていく。

今回はデータベース。

準備

まずはbuild.gradleのdependenciesに以下を追加する。

    implementation("io.micronaut.sql:micronaut-hibernate-jpa")
    implementation("io.micronaut.sql:micronaut-jdbc-hikari")
    runtimeOnly("com.h2database:h2")
    implementation("jakarta.persistence:jakarta.persistence-api:3.1.0")
    annotationProcessor("io.micronaut.data:micronaut-data-hibernate-jpa")
    annotationProcessor("io.micronaut.data:micronaut-data-processor")
    kapt("io.micronaut.data:micronaut-data-processor")
    implementation("io.micronaut.data:micronaut-data-hibernate-jpa")
    implementation("io.micronaut.flyway:micronaut-flyway")
    runtimeOnly("mysql:mysql-connector-java")
    runtimeOnly("org.flywaydb:flyway-mysql")

適当に追加したので何が必要で何が必要じゃないかわかってない。良い子のみんなはプロジェクト作成時に必要なものを選択しておこう。

使うデータベース

データベースには色々あるけど、今回はH2を使う。これが一番楽そうなので。前にSpring Bootを使ったときもH2を使っていたような。

本当はPostgreSQLを使ってみたかったけどぼくのレベルでは無理そうだったので一旦パス。

実装

index.htmlとadd.htmlはそのまま。天気の情報を管理するクラスを用意する。Entity.ktというファイルを作り、中身を以下のようにする。

package com.example

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id

@Entity
data class Weather(@Id @GeneratedValue var id: Long, var city: String, var weather: String)

@Entityというアノテーションをつけることで、データベースとのやりとりがなんかいい感じにできるようになるらしい。

weatherというデータを持つクラス名がWeatherなのよくない命名な気がする。

次はWeatherRepository.ktというファイルを作る。

package com.example

import io.micronaut.data.annotation.Repository
import io.micronaut.data.repository.CrudRepository

@Repository
interface WeatherRepository : CrudRepository<Weather, Long>

CrudRepositoryを継承している。Create, Read, Update, Deleteの頭文字らしい。引数は2つあり、第二引数のLongはIdの型を表している。

最後にHelloController.ktを直していく。

package com.example

import io.micronaut.http.HttpResponse
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Body
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Post
import io.micronaut.views.View
import jakarta.inject.Inject

@Controller
class ViewController {
    @Inject
    lateinit var weatherRepository: WeatherRepository

    @Get("/hello")
    @View("index")
    fun index(): HttpResponse<*> {
        val list = weatherRepository.findAll()
        return HttpResponse.ok(mapOf("dataList" to list))
    }

    @Post(value = "/add", consumes = [MediaType.APPLICATION_FORM_URLENCODED])
    @View("add")
    fun add(@Body("city") city: String, @Body("weather") weather: String): HttpResponse<*> {
        val data = Weather(0, city, weather)
        weatherRepository.save(data)
        return HttpResponse.ok(mapOf("city" to city, "weather" to weather))
    }
}

weatherRepositoryを通じてデータベースとやりとりする。@Injectアノテーションをつけることで面倒なことをいい感じにやってくれる。賢すぎないか。

findAll, saveを使う。CrudRepositoryにある機能。実装する必要もないし、SQLを書く必要もない。楽だね。

問題発生

これで実行してみると、次のようなエラーが表示される。

{"message":"Internal Server Error","embedded":{"errors":[{"message":"Internal Server Error: org.hibernate.InstantiationException: No default constructor for entity: : com.example.Weather"}]},"links":{"self":{"href":"/hello","templated":false}}}

デフォルトコンストラクタがないのが原因らしい。そこで、Entity.ktを次のように直す。

package com.example

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id

@Entity
data class Weather(@Id @GeneratedValue var id: Long = 0, var city: String = "", var weather: String = "")

実行

これで実行する。

動いた!追加ができるようになった。

まとめ

MicronautでH2データベースを使えるようにした。

そろそろ独学の限界がきそう。

参考文献

micronaut-projects.github.io