Skip to content

Spring Data CrudRepository を使用したデータベースアクセス

これは、Spring BootとKotlinの始め方チュートリアルの最終パートです。先に進む前に、以前のステップを完了していることを確認してください。


First step KotlinでSpring Bootプロジェクトを作成する
Second step Spring Bootプロジェクトにデータクラスを追加する
Third step Spring Bootプロジェクトにデータベースサポートを追加する
Fourth step Spring Data CrudRepository を使用したデータベースアクセス

このパートでは、データベースアクセスのために JdbcTemplate の代わりに Spring DataCrudRepository を使用するようにサービス層を移行します。 CrudRepository は、特定のタイプのリポジトリに対する汎用的な CRUD 操作のためのSpring Dataインターフェースです。 データベースとやり取りするためのいくつかのメソッドが標準で提供されています。

アプリケーションを更新する

まず、CrudRepository API で動作するように Message クラスを調整する必要があります。

  1. Message クラスに @Table アノテーションを追加して、データベーステーブルへのマッピングを宣言します。 id フィールドの前に @Id アノテーションを追加します。

    NOTE

    これらのアノテーションには、追加のインポートも必要です。

    kotlin
    // Message.kt
    package com.example.demo
    
    import org.springframework.data.annotation.Id
    import org.springframework.data.relational.core.mapping.Table
    
    @Table("MESSAGES")
    data class Message(@Id val id: String?, val text: String)

    さらに、Message クラスの使用をよりKotlinらしい書き方にするために、 id プロパティのデフォルト値を null に設定し、データクラスプロパティの順序を入れ替えることができます。

    kotlin
    @Table("MESSAGES")
    data class Message(val text: String, @Id val id: String? = null)

    これで、Message クラスの新しいインスタンスを作成する必要がある場合、text プロパティのみをパラメータとして指定できます。

    kotlin
    val message = Message("Hello") // id is null
  2. Message データクラスで動作する CrudRepository のインターフェースを宣言します。MessageRepository.kt ファイルを作成し、以下のコードを追加します。

    kotlin
    // MessageRepository.kt
    package com.example.demo
    
    import org.springframework.data.repository.CrudRepository
    
    interface MessageRepository : CrudRepository<Message, String>
  3. MessageService クラスを更新します。このクラスは、SQLクエリを実行する代わりに MessageRepository を使用するようになります。

    kotlin
    // MessageService.kt
    package com.example.demo
    
    import org.springframework.data.repository.findByIdOrNull
    import org.springframework.stereotype.Service
    
    @Service
    class MessageService(private val db: MessageRepository) {
        fun findMessages(): List<Message> = db.findAll().toList()
    
        fun findMessageById(id: String): Message? = db.findByIdOrNull(id)
    
        fun save(message: Message): Message = db.save(message)
    }
    拡張関数

    findByIdOrNull() 関数は、Spring Data JDBC の CrudRepository インターフェースに対する拡張関数です。

    CrudRepository の save() 関数

    この関数は、新しいオブジェクトがデータベースにIDを持たないという前提で動作します。したがって、挿入時にはIDがnullであるべきです

    IDがnullでない場合、CrudRepository はそのオブジェクトがすでにデータベースに存在するとみなし、これは挿入操作とは対照的に更新操作であると判断します。挿入操作後、id はデータストアによって生成され、Message インスタンスに割り当て直されます。このため、id プロパティは var キーワードを使用して宣言されるべきです。

  4. 挿入されたオブジェクトのIDを生成するようにメッセージテーブルの定義を更新します。id は文字列なので、RANDOM_UUID() 関数を使用してデフォルトでID値を生成できます。

    sql
    -- schema.sql 
    CREATE TABLE IF NOT EXISTS messages (
        id      VARCHAR(60)  DEFAULT RANDOM_UUID() PRIMARY KEY,
        text    VARCHAR      NOT NULL
    );
  5. src/main/resources フォルダにある application.properties ファイル内のデータベース名を更新します。

none
spring.application.name=demo
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:file:./data/testdb2
spring.datasource.username=name
spring.datasource.password=password
spring.sql.init.schema-locations=classpath:schema.sql
spring.sql.init.mode=always

アプリケーションの完全なコードは次のとおりです。

kotlin
// DemoApplication.kt
package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

kotlin
// Message.kt
package com.example.demo

import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table

@Table("MESSAGES")
data class Message(val text: String, @Id val id: String? = null)

kotlin
// MessageRepository.kt
package com.example.demo

import org.springframework.data.repository.CrudRepository

interface MessageRepository : CrudRepository<Message, String>

kotlin
// MessageService.kt
package com.example.demo

import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class MessageService(private val db: MessageRepository) {
    fun findMessages(): List<Message> = db.findAll().toList()

    fun findMessageById(id: String): Message? = db.findByIdOrNull(id)

    fun save(message: Message): Message = db.save(message)
}

kotlin
// MessageController.kt
package com.example.demo

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.net.URI

@RestController
@RequestMapping("/")
class MessageController(private val service: MessageService) {
    @GetMapping
    fun listMessages() = ResponseEntity.ok(service.findMessages())

    @PostMapping
    fun post(@RequestBody message: Message): ResponseEntity<Message> {
        val savedMessage = service.save(message)
        return ResponseEntity.created(URI("/${savedMessage.id}")).body(savedMessage)
    }

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

    private fun Message?.toResponseEntity(): ResponseEntity<Message> =
        // If the message is null (not found), set response code to 404
        this?.let { ResponseEntity.ok(it) } ?: ResponseEntity.notFound().build()
}

アプリケーションを実行する

おめでとうございます!アプリケーションは再び実行準備が整いました。 JdbcTemplateCrudRepository に置き換えた後も、機能は同じままであるため、アプリケーションは以前とまったく同じように動作します。

これで、requests.http ファイルから POSTおよびGET HTTPリクエストを実行して、同じ結果を得ることができます。

次のステップ

Kotlinの機能を探求し、学習の進捗を追跡するのに役立つパーソナル言語マップを入手しましょう。

Get the Kotlin language map