LoginSignup
2

More than 3 years have passed since last update.

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

Last updated at Posted at 2017-12-16

アドベントカレンダーもついに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分はかからなかったですし...。
あと、コントローラ、サービス、リポジトリ、エンティティクラスのアノテーションが用意されているので、この処理はリポジトリで、あの処理はサービスで--と考えなくても役割分担ができてしまい実装がすごく楽でした。

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2