アドベントカレンダーもついに17日目です。
クリスマス一週間前ですが、この調子だと今年もクリぼっち確定ですね~(;´д`)トホホ
普段会社ではLaravelやSAStruts(Java)を触っているんですが、Kotlinでサーバサイドの実装をしたことはなかったので実際にやってみました。
アプリケーション作成
Androidで使うAPIアプリケーションを想定して作成を進めていきます。
試しにユーザデータの照会、ユーザデータの登録のAPIを作ることにします。
Spring Bootのインストール
必要なライブラリをbuild.gradleへ書くのは面倒だなー、と思っていたら下記のサイトを見つけました。アプリケーションのひな形を生成してくれるみたいです。
Generate Projectを押してダウンロードしたzipファイルを解凍し、build.gradleのdependenciesへ下記を追加します。
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
- [バージョン番号]と[説明]の間はアンスコ
_
二つはさまる
-- ユーザテーブル作成
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分はかからなかったですし...。
あと、コントローラ、サービス、リポジトリ、エンティティクラスのアノテーションが用意されているので、この処理はリポジトリで、あの処理はサービスで--と考えなくても役割分担ができてしまい実装がすごく楽でした。