d.sunnyone.org
sunnyone.org

ページ

ラベル Kotlin の投稿を表示しています。 すべての投稿を表示
ラベル Kotlin の投稿を表示しています。 すべての投稿を表示

2018-10-04

Spring Data JDBC で Enum を序数でない数値に変換する

Spring Data JDBC では、デフォルトではenumはnameに変換される。しかし、DBに格納する際は特定のコード(数値)に変換したいことも多いと思うので、その方法について書く。
Kotlinで書いてしまうが、Javaへの読み替えは難しくないと思う。

インタフェースの作成と利用

enumはordinal(序数)を持っているが、それぞれの項目で固定されたコード(数値)を設定したいので、コードを設定できるinterfaceを作成する。

interface CodedEnum {
    val code: Int
}
enum側で使っておく。
enum class Status(override val code: Int): CodedEnum {
    SUCCESS(1),
    FAILURE(2),
}

NamedParameterJdbcTemplateをカスタマイズしてパラメータを差し替える

Spring Data JDBC は内部でSpring JDBC のNamedParameterJdbcTemplateを使っているが、これが@Query で指定したパラメータの変換を担っている。 パラメータをenumで受け取るには、これがCodedEnumを扱えるようになる必要があるので、カスタマイズされたNamedParameterJdbcTemplateを準備する。
対応してる箇所が低いところすぎて気持ち悪いけど、getPreparedStatementCreator() は protected なので、NamedParameterJdbcTemplate的にはここを置き換えることは想定されているんだろうなと思いつつ対応。 enumに限らずここを書き換えたい作業はあると思うので、将来的にはSpring Data JDBC がもっと便利になって、こういう処理はいらなくなるかも。

Converterの追加とCustomizedNamedParameterJdbcTemplateの適用

@EnableJdbcRepositories の @Configuration で、まずnamedParameterJdbcTemplateを差し替える。

そして、値の変換を行うJdbcCustomConversionsにもEnumの変換処理を入れる。基本的には、write方面はinterfaceをそのまま.codeで変換して、readは型がわからないのでfactoryを利用して変換する。 ここでポイントになるのは、Spring Data JDBC の BasicRelationalPersistentPropertyがDB向け変換を行う際にEnumをStringに変換するということ。 これに対応するため、to Stringの変換を用意しておく。

 これで、codeで設定した値が利用されるようになる。

2017-10-20

Kotlinでコマンドラインツールを作成する(Gradle/fat jar)

Kotlinを始めるにはコマンドラインツールを作ってみてはと言いつつ、そういう話あんまりなくない?と思ったので書くことにした。 Gradleでfat jarを生成し、javaがあれば実行できるようにするところまで。

sdkmanでGradleをインストール

http://sdkman.io/install.html からsdkmanをインストールして、gradleをインストールする。
$ sdk install gradle

gradle initで雛形の作成

適当なディレクトリを作成し、gradle initでbuild.gradle他ファイルを生成する。

$ mkdir kotlin-cmd-example
$ cd kotlin-cmd-example
$ gradle init

build.gradleの編集

Using Gradle - Kotlin Programming Languageを参考にして、build.gradleを記述する。

kotlin-gradle-pluginを適用して、kotlin-stdlib他必要なライブラリを追加すればOK。

ここではlog出力のためのlogbackのほか、fat jarを生成するための johnrengelman/shadow を使っている。 shadowの設定については Shadow Plugin User Guide & Examples を。

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.1.51'
    id 'com.github.johnrengelman.shadow' version '2.0.1'
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.jetbrains.kotlin:kotlin-stdlib-jre8'

    compile 'org.slf4j:slf4j-api:1.7.25'
    compile 'org.slf4j:jcl-over-slf4j:1.7.25'
    compile 'org.slf4j:jul-to-slf4j:1.7.25'
    compile 'ch.qos.logback:logback-classic:1.2.3'
}

apply plugin: 'application'
mainClassName = "com.example.kotlincmdexample.KotlinCmdExampleKt"

注意点としては、特に設定しないとJava側からはクラス名にはKtがついているのでつける。

src/main/kotlin に Kotlin ソースコードを作成

デフォルトではソースの場所はsrc/main/kotlinなので、ここにソースコードを配置していく。 IntelliJ IDEAであれば、build.gradleを開けば開始できる。

src/main/kotlin/com/example/kotlincmdexample/KotlinCmdExample.kt

package com.example.kotlincmdexample

import org.slf4j.LoggerFactory

fun main(args: Array<String>) {
    val log = LoggerFactory.getLogger("com.example.kotlincmdexample.KotlinCmdExample")

    if (args.isEmpty()) {
        println("Usage: kotlin-cmd-example name")
        return
    }

    log.info("Hello, {}", args[0])
}

あえてclassを作ってないのでgetLoggerが冗長な感じがするが、中途半端なのでloggerの名前を適当にしてもいいし、クラスを作って渡すかしたほうがいいかも。

./gradlew runでテスト実行

./gradlew runすれば実行できる。IDEAであれば右クリックしてRunあるいはDebugでもOK。

$ ./gradlew run 

> Task :run
Usage: kotlin-cmd-example name


BUILD SUCCESSFUL in 0s
2 actionable tasks: 1 executed, 1 up-to-date

./gradlew buildでビルド(jar生成)

./gradlew buildするとbuild/distributionsにkotlin-cmd-example-shadow.tarと.zipが入っているので、これを展開すると起動スクリプトがあるので実行できる。

build/libsに-all.jarがあるので、java -jarで直接起動することも可能。

$ java -jar build/libs/kotlin-cmd-example-all.jar 
Usage: kotlin-cmd-example name

$ java -jar build/libs/kotlin-cmd-example-all.jar MyFirstKotlinProject
19:49:00.796 [main] INFO com.example.kotlincmdexample.KotlinCmdExample - Hello, MyFirstKotlinProject

今回のプロジェクト

GitHubのsunnyone/kotlin-cmd-exampleに置いておくので、参考になれば。

2017-06-27

Kotlin で引数の名前つきでの呼び出しを強制する裏ワザ

小ネタ。

Kotlinでは引数を名前つきで呼び出せる。なしでもOK。
fun add(x: Int, y: Int) = x + y

fun f() {
    add(1, 2)
    add(x=1, y=2)
    add(1, y=2)
}

名前付きで呼んでほしいときはどうするか?名前つきにしてほしいところの前にvararg Voidをおく。

fun add(vararg useNamed: Void, x: Int, y: Int) = x + y

fun f() {
    // add(1, 2) // コンパイルエラー!
    add(x=1, y=2)
    // add(1, y=2) // コンパイルエラー!
}

途中からでもOK。
fun add(x: Int, vararg useNamed: Void, y: Int) = x + y

fun f() {
    // add(1, 2) // コンパイルエラー!
    add(x=1, y=2)
    add(1, y=2)
}

…いやいやだめだろこれ。もっといい方法があれば教えて下さい。
参考:How can I force calls to some constructors/functions to use named arguments?

2016-08-08

Java → Kotlin 連携時は検査例外に注意する

Kotlinには検査例外はない(Kotlin does not have checked exceptions)。しかし、Javaにおいては検査例外として取り扱われるクラスを普通にthrowすることができる。

コード例にすると、以下の通り。




もともとJavaでも宣言していなくても検査例外を投げてくるときは投げてくる(ERR06-J. 宣言されていないチェック例外をスローしない)が、throwsが目印にもならないという点で注意が必要である。