diadia

興味があることをやってみる。自分のメモを残しておきます。

バックエンドでKotlinをさわる

お世話になった本は以下。

入門!実践!サーバーサイドKotlin (技術の泉シリーズ(NextPublishing)) | 横山 恭大 | 工学 | Kindleストア | Amazon

androidアプリケーションでKotlinは使っていたけれども、主戦場にしたいバックエンドでKotlinが使えてないのは残念なので触ってみる。あとwebフレームワーク自体はdjangoしか触ったことがないので、他のフレームワークを触ることでどういう考え方が共通しているのか確認したい思惑がある。

Springframeworkについて

spring frameworkフレームワークを束ねているものらしい。
その中から使いたいフレームワークを選択して構築していくらしい。
で、選択したフレームワークを実際に使えるように設定するには諸設定が必要になるようでそれが大変のよう。
その諸設定を簡単に解決する方法としてSpring Bootというフレームワークを使うようだ。これはSpring Initializerでファイルを生成し、それを使うとすぐ開発できるようになるといったもののようだ。

ビルドシステムについて

ビルドシステムはライブラリの依存関係を調整する機能とコンパイルする機能を持つ ビルドシステムにはAnt, Maven, Gradleといったものが存在する。JVM系の言語で開発する際にGradleといったビルドシステムにお世話になるのかなあと少ない経験から推測中。。。

プロジェクトの始め方

spring bootを使う場合プロジェクトの開始方法としてSpringInitializrを使う方法がある。 これは
https://start.spring.io/ にアクセスしてプロジェクトの条件を設定してやり、zipファイルを生成し、ダウンロードする。これを解凍し、そのディレクトリをIDEで開く(import)形で使用することだと分かった。 設定項目であるが、

設定項目 選択肢 自分が今回設定した内容
project Maven project またはGradle project Gradle projectを選択
language Java, GrrovyまたはKotlin Kotlinを選択
Spring Boot 様々なバージョンがある バージョン2.3.4を選択

今気になったこと

  • どういう感じでリクエストを受け入れるのか。 -> RequestMappingアノテーションを使ってurlを設定
  • どうやってhtmlを返すのか?そしてそれはどこに格納するのか? -> いわゆるControllerを使うと設定できる。当初はurls.py, views.pyといったファイルで明確に分けられているものかと思ったけどそうではなく、url解決と何を返すかは同一ファイルで併存する形式を取る。

@RestControllerについて

「@RestController」は、viewに遷移せずメソッドの戻り値がそのままHTTPリクエストのレスポンスとなります。このため、APIのようにデータだけ取得したい場合に用いられることが多いです。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.739-741). Kindle 版.

つまり@RestControllerはDjangoでいうHttpResponseクラスにが匹敵するものであると認識すればよいだろうか。

@RequestMappingについて

「@RequestMapping」についてです。こちらはリクエストを受け付けるパスを指定します。今回は「/」なので「http://localhost:8080」で受け付けます。「/hoge」にすると「http://localhost:8080/hoge」になります。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.744-746). Kindle 版.

これはDjangoでいうurls.pyに該当するところだと思えば良いと思う。

djangoの場合はurlsの解決とその挙動を厳密に分けていたわけだけれどもSpring Bootはそこをきっちり分けていないことが特徴だとわかる。 またアノテーションを付けてurlsの設定をする辺りはretrofit2のような感じで、全然違和感はない。むしろ親しみがあるやり方だ。

import org.springframework.webのimportが上手くいかない

build.gradle.ktsに依存関係のライブラリを記述するスペース(dependencies)があるが、そこに下記の修正を加えること。

//implementation("org.springframework.boot:spring-boot-starter")        //コメントアウト
implementation("org.springframework.boot:spring-boot-starter-web") //追加

上記のライブラリを追加することで@RestControllerや@RequestMappingを使えるようになる。

Thymeleafについて

Spring Bootに使われるThymeleafはHTMLを返す役割を果たすことが分かった。djangoでいうrender関数のようなものだと現状理解して進めている。

@Controllerについて

HTMLを返す役割を担う。return でhtmlファイルを返す。

@GetMappingについて

RequestMappingとGetMappingの使い分けについてだけれども、おそらくRequestMappingの方はリクエストメソッドのGET以外に対応できると思われるけど、GetMappingはGetのみの対応になると思われる。GetMappingは@RequestMapping(method = RequestMethod.GET)のショートカットであるという説明している文章も見た。

Spring Boot組み込みのデータベースを使う際の手続き

  • 組み込みのデータベース=H2database
  • javaでDB操作(H2database) -> JPAを使う
プロジェクト作成時に行うこと

Spring Initilizerでプロジェクト作成時に以下を追加する。

  • Spring Web
  • Thymeleaf(template engine)
  • H2 Database(SQL)
  • Spring Data JPA(SQL)
DB接続情報

DB接続情報はapplication.propertiesに記載する。

spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/db_example
spring.datasource.username=username
spring.datasource.password=password
DBテーブルを作成するには?

djangoの場合はmodels.pyに作成したいmodel(テーブル名)をmodels.Modelを継承した形式で定義した。 一方kotlinを使ったH2databaseの場合には、data classを使って定義する。

package com.example.hello_db_app.demo_db

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

@Entity //このアノテーションがあるとテーブルが作成される
data class User(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Int = 0,
        var name: String = ""
)

@Autowiredについて

依存性注入のアノテーションであり、該当するクラスのインスタンスフレームワークが自動でnewして代入してくれます。今回は「UserRepository」のインスタンスになります。こうすることでテストを書くときにモックに置き換えやすくなったり、共通処理を追加しやすくなったりします。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.915-918). Kindle 版.

とのことだったが、よく分からなかった。後にわかるだろうってことで今はパスする

@PostMappingについて

GetMappingと同じ類。RequestMappingでかつPOSTメソッドのみに対応するもの。

@RequestParamについて

これは指定した引数がリクエストパラメーターであることを宣言します。そのため「http://localhost:8080?id=10」として「@RequestParamid:Int」とするとidに10が渡ってきます。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.922-924). Kindle 版.

djangoの場合では

class HogeView(View):
    def get(self, request, *args, **kwargs)
        ...

のような形でself.requestから取り出したり、urlパラメーターから取り出す処理をしていたけど入れ込むことに関しては特に意識しないでやっていた。 Spring Bootの場合には、リクエストパラメータを指定しなければいけないってことが違いであり、ポイントなのであろう。

Spring Bootでテストコード

やること。

  • テストDBの準備
テストDBの準備

DBは/src/test/resources/application.propertiesの形で設置する。application.propertiesはオリジナルのファイルをコピーしてくれば良い。
ファイルには以下の内容を記述する。

spring.jpa.hibernate.ddl-auto=create
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/test_db_example
spring.datasource.username=username
spring.datasource.password=password

spring.jpa.hibernate.ddl-autoの値をcreateにすることでテスト実行時に新たなテーブルが作成され、すべてのテストを0から始めることができる。spring.datasource.urlの値はテストデータベースのurlでこれは任意?の値で良いと思われる。

@RunWith(SpringJUnit4ClassRunner::class)について

これはテスト用のライブラリにJUnit4を使用するという宣言です。SpringBootはデフォルトでJUnit4が組み込まれています。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.1005-1006). Kindle 版.

@SpringBootTestについて

これはテスト時にSpringBootが必要な機能を使用する宣言です。今回のテストコードではモックの生成に使用します。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.1006-1008). Kindle 版.

@Beforeについて

テストメソッドが実行される前に呼び出されることを宣言します。これによりテスト前の下準備を済ませます。今回はモックを生成しています。

横山 恭大. 入門!実践!サーバーサイドKotlin (Japanese Edition) (Kindle の位置No.1008-1010). Kindle 版.

@Columnについて

このアノテーションはエンティティの定義(モデル)の定義で使われる。クラスのプロパティ名とテーブルのフィールド名が異なるときに使われる。

server_side_kotlin_bbs_sample/Article.kt at master · fortegp05/server_side_kotlin_bbs_sample · GitHub

package com.example.app.bbs.domain.entity

import java.util.*
import javax.persistence.*

@Entity
data class Article (
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id : Int = 0,
        var name : String = "",
        var title : String = "",
        var contents : String = "",
        @Column(name = "article_key")
        var articleKey : String = "",
        @Column(name = "register_at")
        var registerAt : Date = Date(),
        @Column(name = "update_at")
        var updateAt : Date = Date(),
        @Column(name = "user_id")
        var userId : Int? = null
)

Spring BootのDBまわりについて推測

Spring BootでもORMをやりたい場合には、djangoっぽい下準備が必要になる。 まずモデル。モデルはテーブルに一致したモデルを作成しなければならない。このモデルに対応する言葉としてSping BootではEntityという言葉が使われているけれども要はmodelってことなのだろう。 djangoの場合はモデルさえ作ったら、views.pyで各モデルのインスタンスを呼び出すなり、初期化生成して、その後save()メソッドを使ってデータを永続化していた。Spring Bootではこの点で少し異なりEntityを定義しただけではデータを永続化する処理をいきなりすることができない。そこで必要な概念がRepositoryという概念。これは要するに上で作ったEntityを利用したインターフェースを作り、ORMを利用する下準備をする。djangoではdjango.models.Modelを継承したクラスさえ作れば良かったけれども、Spring Bootではまずテーブルに対応するEntityを作成する。そしてデータ操作を可能にするために作成したEntityを利用してRepositryを作る。こういった差異があるのではないかと現状推測している。 このRepositoryには種類があり、JpaRepositoryとCrudRepositoryの種類が存在していることを確認している。

参考

SpringBoot JPARepository 使ってみた | Solowareの技術目録