Kotlin × Spring Boot でDBからid指定してデータを取得する

こんにちは、白々さじきです。

今回は、前回の続きでKotlin × Spring Boot を使用してID を指定して単体のデータを取得できる API(GET /{id} を実装する方法を解説します。

今回のチュートリアルの参考ページはこちらです。
Spring Boot プロジェクトにidからデータを取得するAPIを追加

MessageServiceの変更

MessageServiceを下記のように変更してください。

// MessageService.kt
package demo

import org.springframework.stereotype.Service
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.core.query
import java.util.*

@Service
class MessageService(private val db: JdbcTemplate) {

    fun findMessages(): List<Message> = db.query("select * from messages") { response, _ ->
        Message(response.getString("id"), response.getString("text"))
    }

    fun findMessageById(id: String): Message? = db.query("select * from messages where id = ?", id) { response, _ ->
        Message(response.getString("id"), response.getString("text"))
    }.singleOrNull()

    fun save(message: Message): Message {
        val id = message.id ?: UUID.randomUUID().toString() // Generate new id if it is null
        db.update(
            "insert into messages values ( ?, ? )",
            id, message.text
        )
        return message.copy(id = id) // Return a copy of the message with the new id
    }
}

.query() 関数は、指定したIDのメッセージを取得するためのKotlin の拡張関数 であり、Spring Framework によって提供されているため、import org.springframework.jdbc.core.query を追加を忘れないようにしてください!

MessageControllerの追加

MessageControllerに下記を追加してください。

import org.springframework.web.bind.annotation.PathVariable

class MessageControllerの中に下記を追加してください。

@GetMapping("/{id}")
fun getMessage(@PathVariable id: String): ResponseEntity<Message> =
    service.findMessageById(id).toResponseEntity()

private fun Message?.toResponseEntity(): ResponseEntity<Message> =
    this?.let { ResponseEntity.ok(it) } ?: ResponseEntity.notFound().build()

今回追加・変更したコードに関しての解説

チュートリアルに今回追加・変更したコードに関しての解説があったので記載します。

コンテキストパスから値を取得する

Spring Framework は、@GetMapping("/{id}") アノテーションを付けたメソッドを使用して、URL のパス(コンテキストパス)から id を取得できます。
また、Controllerのメソッドで@PathVariable を使用すると、URLのパスから取得した id をメソッドの引数として受け取ることができるようになります。
今回追加した関数では、MessageService のメソッドを呼び出して、指定された ID のメッセージを取得 します。

query() 関数について

query() 関数は、下記の3つの引数を取ります。

  1. SQLクエリ文字列(DBから検索するためのSQLを書く)
  2. id(String型のパラメータ)
  3. RowMapper(今回は、ラムダ式で実装)
    • DBのレコードとJavaオブジェクトのマッピングを行うクラス

query() 関数の 2 番目の引数(id)は vararg(可変長引数) として宣言されています。
Kotlin では vararg の引数をパラメータリストの最後に置く必要はないそうです。

ResponseEntityについて

ResponseEntity は、HTTP レスポンス全体(ステータスコード、ヘッダー、ボディ)を表すクラス です。クライアントにカスタマイズしたHTTPレスポンスを返す際に使用することができるため、レスポンスの内容を細かく設定できます。

Nullable レシーバーを持つ拡張関数

Kotlin では、拡張関数を nullable な型に対しても定義 できます。
レシーバーが null の場合、その拡張関数の thisnull になるため、nullable なレシーバーを持つ拡張関数を定義する場合は、this == null のチェックを行うのが推奨 されているそうです。

また、?.(null安全呼び出し演算子)を使用して null の場合の処理を行うことも可能 です。
例えば、以下の toResponseEntity() 関数では、thisnull の場合 404 Not Found を返し、
そうでない場合は 200 OK でメッセージを返します。

this?.let { ResponseEntity.ok(it) } ?: ResponseEntity.notFound().build()

HTTPリクエストのテスト

前回同様にApidog Fast Requestを使用してテストを実行します。

全体取得を実行し、idを取得後そのidをURLに入れることで下記のようにからデータを取得することができます。

おまけ

追加でtextからデータを取得するAPIを作成してみました。

MessageServiceに追加

MessageServiceに下記の関数を追加してください!

    fun findMessageByText(text: String): Message? = db.query("select * from messages where text = ?", text) { response, _ ->
        Message(response.getString("id"), response.getString("text"))
    }.singleOrNull()

MessageControllerの追加

MessageControllerに下記の関数を追加してください!@GetMappingの中の("text/{text}")の最初のtextの部分に関しては、パスをユニークにするために置いているだけなので自由に変更してかまいません。

@GetMapping("text/{text}")
    fun getMessageText(@PathVariable text: String): ResponseEntity<Message> =
        service.findMessageByText(text).toResponseEntity()

HTTPリクエストのテスト

textの部分をURLに入れることで下記のようにからデータを取得することができます。

まとめ

今回は、パスパラメータのデータを元に検索を行うAPIの作成を行いました。

おまけ機能を実装する際、”/{text}” というエンドポイントを誤って定義してしまい、既存のルートと競合していることに後から気づきました。次から気を付けようと思います!

次回は、Spring Data CrudRepository を使って、より簡単にデータベースアクセスを行う方法を学びたいと思います!

この記事が誰かの役に立てれば幸いです。

サポートのお願い

下記リンクからお買い物いただけると、ブログ運営のための費用が増え、有料サービスを利用した記事作成が可能になります。ご協力よろしくお願いします!

コメント

タイトルとURLをコピーしました