前回に引き続きKotlinでsqlファイルを実行する実装を模索しています。
またしても学びごとがあったので記しておきます。
こちらの続きでSpringBootを利用しています。
利用してるgradleの設定、Repositoryクラス諸々も、前回の記事と同様のものを使っています。
今回やりたいこと
例えば以下のようなSQLファイルを実行した結果をそれぞれ出力したい。
select count(1) from staff; --1件ある
select count(1) from food; --3件ある
つまりファイル中の複数のcountクエリをそれぞれ実行して、結果を取得したい。
やったこと
- splitの挙動について
- 正規表現で区切り文字を含む方法
これらを踏まえてやりたいことを叶えていきます。
splitの挙動について
;区切りで文字列を取得する場合、Java同様こんなイメージをしていました。
val list = "aaa;bbb;ccc;".split(";")
println(list)
println(list.size)
出力結果はこうなりました。
[aaa, bbb, ccc, ]
4
一番後ろの;で区切った時の空文字列分の値が入っちゃってますね。
Javaでは
Javaでは第二引数に0を設定すると末尾の空文字列が廃棄されます。
しかし、、
Kotlinでは
when the limit is not specified or specified as 0, this function doesn't drop trailing empty strings from the result.
末尾の空文字列は除去しませんよとご丁寧に書いてありました。
結論:JavaとKotlinのsplitの挙動は違う
ので、手動で空文字列を消すことにしました。
以下末尾の空文字列を根こそぎ消すというもの。
val list = "aaa;bbb;ccc".split(";")
println(list.dropLastWhile { it.isBlank() })
println(list.size)
出力結果
[aaa, bbb, ccc]
3
こんなに簡単に書けるなんて。Kotlinすばら。
正規表現で区切り文字を含む方法
今回はSQLの区切り文字である;を含めたかったので
以下のような正規表現を使うことにしました。
Regex("(?<=;)")
直前に;がある文字列を表す正規表現です。
Regexにもsplit関数が用意されているようなのでこれを使います。
Stringのsplit同様、第二引数で空文字列を除去することはできませんでした。
val reg = Regex("(?<=;)")
print(reg.split("aaa;bbb;ccc;", 0))
出力結果
[aaa;, bbb;, ccc;, ]
まとめて適用して実行する
以下のようにServiceクラスにこれらを実装していきます。
@Service
class SakuraService {
/**
SQLファイル内のクエリを実行する
@param ファイルパス
@return SQLクエリ文字列のリスト
*/
fun getSqlQueryList(filepath:String):List<String>{
val fileString = Files.readString(Paths.get(filepath))
//区切り文字を含む文字列を取得するための正規表現
val reg = Regex("(?<=;)")
//Kotlinのsplitは末尾空白列除去オプションがないため手動で削除
return reg.split(fileString).dropLastWhile { it.isBlank() }
}
}
実行するApplicatioクラス、main関数はこんな感じです。
package com.example.sakura
import com.example.sakura.repository.SakuraRepository
import com.example.sakura.service.SakuraService
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.jdbc.core.JdbcTemplate
import java.io.File
@SpringBootApplication
class SakuraApplication(val jdbcTemplate: JdbcTemplate) : CommandLineRunner {
override fun run(vararg args: String?) {
val service = SakuraService()
val repository = SakuraRepository(jdbcTemplate)
//sqlファイルからクエリの一覧を抽出する
val sqlList = service.getSqlQueryList("src/main/resources/sql/test.sql")
for (sql in sqlList) {
println(sql)
//sql実行結果
println(repository.count(sql))
}
}
}
fun main(args: Array<out String?>) {
SpringApplication.run(SakuraApplication::class.java, *args)
}
JdbcTemplateについては先の記事に記載してますので参照ください。
実行した出力結果がこちら
select count(1) from staff;
1
select count(1) from food;
3
出来上がり。
最後に
区切り文字の;がなくてもクエリごとに文字列切ってれば実行できたりします。
SQL実行時に区切り文字あった方が安心だよね。っていう気持ちでくっつけてます。