Edited at

Spring Boot(Kotlin) + Flywayでサーバサイドアプリケーション作ってみた

アドベントカレンダーもついに17日目です。

クリスマス一週間前ですが、この調子だと今年もクリぼっち確定ですね~(;´д`)トホホ

普段会社ではLaravelやSAStruts(Java)を触っているんですが、Kotlinでサーバサイドの実装をしたことはなかったので実際にやってみました。


アプリケーション作成

Androidで使うAPIアプリケーションを想定して作成を進めていきます。

試しにユーザデータの照会、ユーザデータの登録のAPIを作ることにします。


Spring Bootのインストール

必要なライブラリをbuild.gradleへ書くのは面倒だなー、と思っていたら下記のサイトを見つけました。アプリケーションのひな形を生成してくれるみたいです。

SPRING INITIALIZR

Generate Projectを押してダウンロードしたzipファイルを解凍し、build.gradleのdependenciesへ下記を追加します。


build.gradle

apply plugin: 'flyway'

:
dependencies {
:
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile 'mysql:mysql-connector-java:5.1.6'
compile group: 'org.flywaydb', name: 'flyway-core', version: '4.1.0'
:
}


FlywayによるDBマイグレーション

Laravelには便利なマイグレーションの仕組みがありましたが、Spring Bootではどうマイグレーションすればよいのか分かりません。調べてみると、FlywayというDBマイグレーションツールが見つかりました。

resource/db/migration配下へSQLファイルを追加します。

SQLのファイルには命名規則が決まっているので注意が必要です。


マイグレーションファイル命名規則

V[バージョン番号]__[説明].sql


  • 先頭はV

  • [バージョン番号]は1.0.0や1_0など._を組み合わせてバージョンを表現した文字列が入ります。

  • [説明]何でもOK

  • [バージョン番号]と[説明]の間はアンスコ_二つはさまる


V1.0.0__create_table.sql

-- ユーザテーブル作成

CREATE TABLE IF NOT EXISTS users
(
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
is_deleted BOOLEAN NOT NULL DEFAULT 0,
created_on DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_on DATETIME ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB;

-- ユーザデータ初期化
INSERT INTO users (name, password) VALUES ("もりもり", "password");



DB接続先情報を追記

application.propertiesに下記を追加して、DBに接続できる状態にしておきます。

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://[DBホスト名]:3306/[DB名]
spring.datasource.username=[DBユーザ名]
spring.datasource.password=[DBパスワード]


Entity

DBから取得したデータを格納するEntityクラスを用意します。

@EntityでこのクラスがEntityであることを定義、@Idでプライマリキーにするプロパティを指定します。

dataクラスで定義する場合、デフォルト値を入れておかないとInstantiationException: No default constructor for entityが発生するので注意が必要です。

@Tableにはテーブル名を指定します。

もし論理削除を使いたいときは@Whereに論理削除の状態を指定します。

@Entity

@Table(name="users")
@Where(clause="is_deleted=0")
data class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id", unique = true, nullable = false)
var id: Long = -1,
@Column(name="name")
var name: String = "",
@Column(name="password")
var password: String = "",
@Column(name="created_on")
var createdOn: Date? = null,
@Column(name="updated_on")
var updatedOn:Date? = null
)


Repository

JpaRepositoryを利用してRepositoryクラスを作成します。Domaなどの選択肢もありましたが、なるべく生のSQLを書きたくなかったのでJpaRepositoryを利用することにしました。

import org.springframework.data.jpa.repository.JpaRepository

import org.springframework.stereotype.Repository
import com.example.User

@Repository
interface UserRepository: JpaRepository<User, Long> {}


Service

@ServiceをつけてServiceクラスであることを定義します。 @Autowiredをコンストラクタの前に付けて、クラスで使用するRepositoryクラスがDIされるようにします。

Kotlinの場合は@Autowiredをつけなくても、コンストラクタ引数に Beanが指定されている場合は、自動的にコンストラクタインジェクションになります。

@Service

class UserService(private val userRepository: UserRepository) {

fun findById(id: Long): User {
return userRepository.getOne(id)
}

fun findAll(): MutableList<User> {
return userRepository.findAll()
}

fun register(user: User): User {
return userRepository.save(user)
}
}


Applicationクラス

@SpringBootApplicationを付けたクラスを用意します。

@SpringBootApplication

open class Application {
companion object {
@JvmStatic fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
}
}

このApplicationクラスを配置することで、配置したパッケージ以下がコンポーネントスキャン対象になり、@Autowiredが使えるようになります。パッケージ対象外をコンポーネントスキャンしたい場合は@ComponentScan([package])とする必要があるみたいです。


Controllerクラス

今回はAndroidアプリで使用するAPIサーバを想定しているので、レスポンスはJsonで返すことにします。

@RestControllerをクラス名に付与することで、各メソッドはレスポンスをJsonとして返すようになります。

@Controllerというアノテーションもありますが、そちらは通常のViewを必要とするWebアプリケーションで使用しましょう。

@RestController

@RequestMapping(path= arrayOf("/"))
class UserController(private val userService: UserService) {
fun index(): List<User> {
return userService.findAll()
}

@RequestMapping(path= arrayOf("{id}"), method= arrayOf(RequestMethod.GET))
fun show(@PathVariable id: Long): User {
return userService.findById(id)
}

@RequestMapping(path= arrayOf("/"), method= arrayOf(RequestMethod.POST), params = arrayOf("name", "password"))
@ResponseStatus(HttpStatus.CREATED)
fun create(@RequestParam name: String, @RequestParam password: String): User {
val user = User(0, name, password, null, null)
return userService.register(user)
}
}

@RequestMappingのpathには対応するパスを指定します。pathには複数のパスを指定することができます。


起動するぞーーー!

下記のコマンドを実行し、ビルトインサーバを立ち上げます。

このタイミングで自動的にFlywayのマイグレーションが実行されます。

$ gradlew bootRun

https://localhost:8080 へアクセスするとユーザ一覧がJsonで返ってくることが確認できるはずです....!


所感

SpringでのMVC開発はかなり簡単だなーという印象です。

一つコントローラを追加して、実際に動作確認するまで10分はかからなかったですし...。

あと、コントローラ、サービス、リポジトリ、エンティティクラスのアノテーションが用意されているので、この処理はリポジトリで、あの処理はサービスで--と考えなくても役割分担ができてしまい実装がすごく楽でした。


参考文献&役に立った記事