少し前までは多少プログラミングを仕事でもしていましたが、最近は仕事でできていないのでせっかくなので spring-boot と kotlin で自作アプリ用の API を作ろうと思って datastore について再度チュートリアルから始めてみました。
チュートリアルはインタラクティブ形式のCUIアプリを作る Google が公式で用意しているものを利用しています。
Java で書かれているので、Kotlin に書き換えてます。
■ チュートリアル
環境
macOS Big Sur 11.1
Intellij Community 2020.3
チュートリアル
以下のステップでチュートリアルを進めていきますが、kotlin に変更した箇所のみピックアップします。
1. Overview
2. Setup and Requirements(プロジェクトの作成とセットアップ)
3. Initialize Cloud Datastore(Datastore初期化)
4. Bootstrap a new Spring Boot Java Application(spring-bootプロジェクト作成)
5. Create the Book class(Book Entity の作成)
6. Create the BookRepository interface(Book Entity 操作用 Rpository作成)
7. Create the interactive CLI application(Shell 実行用処理作成)
8. Run the application(アプリケーション実行)
9. See what is stored in Datastore using web interface(登録内容の確認)
10. Clean up(削除)
11. Congratulations!
チュートリアルとの変更点
4. Bootstrap a new Spring Boot Java Application(spring-bootプロジェクト作成)
kotlin を利用したかったので、チュートリアルにある、spring initializr( https://start.spring.io/ )のページから以下の設定でダウンロードしました。
特にこだわりはないです。
- Project:Gradle Project
- Language:Kotlin
- Spring Boot:2.5.0
- Project Metadata(Packaging):Jar
- Project Metadata(Java):11
チュートリアルでは、maven を利用していますが、gradle でプロジェクト作ったので依存の設定を変更しています。
dependencies {
implementation("org.springframework.cloud:spring-cloud-gcp-starter-data-datastore:1.2.6.RELEASE")
implementation("org.springframework.shell:spring-shell-starter:2.0.0.RELEASE")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
5. Create the Book class(Book Entity の作成)
チュートリアルが Java のコードなので、Kotlin ように書き換えます。
コードが短くなってみやすくていいですね!
package app.noz.ws.example.datastore
import org.springframework.cloud.gcp.data.datastore.core.mapping.Entity
import org.springframework.data.annotation.Id
@Entity(name = "books")
class Book(@Id var id: Long? = null, var title: String, var author: String, var year: Int) {
override fun toString(): String {
return "Book(id=$id, title='$title', author='$author', year=$year)"
}
}
6. Create the BookRepository interface(Book Entity 操作用 Rpository作成)
同様に Kotlin に書き換えます。
package app.noz.ws.example.datastore
import org.springframework.cloud.gcp.data.datastore.repository.DatastoreRepository
interface BookRepository: DatastoreRepository<Book, Long> {
fun findByAuthor(author: String): List<Book>
fun findByYearGreaterThan(year: Int): List<Book>
fun findByAuthorAndYear(author: String, year: Int): List<Book>
}
7. Create the interactive CLI application(Shell 実行)
同様に Kotlin に書き換えます。
package app.noz.ws.example.datastore
import com.google.common.collect.Lists
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.shell.standard.ShellComponent
import org.springframework.shell.standard.ShellMethod
@ShellComponent
@SpringBootApplication
class DatastoreApplication {
@Autowired
private lateinit var bookRepository: BookRepository
@ShellMethod("Saves a book to Cloud Datastore: save-book <title> <author> <year>")
fun saveBook(title: String, author: String, year: Int): String {
val savedBook = this.bookRepository.save(Book(title = title, author = author, year = year))
return savedBook.toString()
}
@ShellMethod("Loads all books")
fun findAllBooks(): String {
val books = this.bookRepository.findAll()
return Lists.newArrayList(books).toString()
}
@ShellMethod("Loads books by author: find-by-author <author>")
fun findByAuthor(author: String): String {
val books = bookRepository.findByAuthor(author)
return books.toString()
}
@ShellMethod("Loads books published after a given year: find-by-year-after <year>")
fun findByYearAfter(year: Int): String {
val books = bookRepository.findByYearGreaterThan(year)
return books.toString()
}
@ShellMethod("Loads books by author and year: find-by-author-year <author> <year>")
fun findByAuthorYear(author: String, year: Int): String {
val books = bookRepository.findByAuthorAndYear(author, year)
return books.toString()
}
@ShellMethod("Removes all books")
fun removeAllBooks() {
bookRepository.deleteAll()
}
}
fun main(args: Array<String>) {
runApplication<DatastoreApplication>(*args)
}
8. Run the application
コマンド経由で実行するとコンソールログが出ておかしくなってしまうため、Intellij から実行しました。
緑の「▶︎」をクリックして実行します。
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0-SNAPSHOT)
2021-01-16 13:47:39.282 INFO 78351 --- [ main] a.n.w.e.d.DatastoreApplicationKt : Starting DatastoreApplicationKt using Java 14.0.1 on MacBook-Pro.local with PID 78351 (/Users/yasu/Downloads/datastore/build/classes/kotlin/main started by yasu in /Users/yasu/Downloads/datastore)
2021-01-16 13:47:39.285 INFO 78351 --- [ main] a.n.w.e.d.DatastoreApplicationKt : No active profile set, falling back to default profiles: default
2021-01-16 13:47:40.118 INFO 78351 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Datastore repositories in DEFAULT mode.
2021-01-16 13:47:40.175 INFO 78351 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 53 ms. Found 1 Datastore repository interfaces.
2021-01-16 13:47:40.767 INFO 78351 --- [ main] o.s.c.g.a.c.GcpContextAutoConfiguration : The default project ID is api-test-300213
2021-01-16 13:47:40.845 INFO 78351 --- [ main] o.s.c.g.core.DefaultCredentialsProvider : Default credentials provider for service account todo-test@api-test-300213.iam.gserviceaccount.com
2021-01-16 13:47:40.846 INFO 78351 --- [ main] o.s.c.g.core.DefaultCredentialsProvider : Scopes in use by default credentials: [https://www.googleapis.com/auth/pubsub, https://www.googleapis.com/auth/spanner.admin, https://www.googleapis.com/auth/spanner.data, https://www.googleapis.com/auth/datastore, https://www.googleapis.com/auth/sqlservice.admin, https://www.googleapis.com/auth/devstorage.read_only, https://www.googleapis.com/auth/devstorage.read_write, https://www.googleapis.com/auth/cloudruntimeconfig, https://www.googleapis.com/auth/trace.append, https://www.googleapis.com/auth/cloud-platform, https://www.googleapis.com/auth/cloud-vision, https://www.googleapis.com/auth/bigquery, https://www.googleapis.com/auth/monitoring.write]
2021-01-16 13:47:42.777 WARN 78351 --- [ main] org.jline : Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)
2021-01-16 13:47:42.937 INFO 78351 --- [ main] a.n.w.e.d.DatastoreApplicationKt : Started DatastoreApplicationKt in 4.112 seconds (JVM running for 5.038)
shell:>
Help コマンドの実行
shell:>help
AVAILABLE COMMANDS
Built-In Commands
clear: Clear the shell screen.
exit, quit: Exit the shell.
help: Display help about available commands.
script: Read and execute commands from a file.
stacktrace: Display the full stacktrace of the last error.
Datastore Application
find-all-books: Loads all books
find-by-author: Loads books by author: find-by-author <author>
find-by-author-year: Loads books by author and year: find-by-author-year <author> <year>
find-by-year-after: Loads books published after a given year: find-by-year-after <year>
remove-all-books: Removes all books
save-book: Saves a book to Cloud Datastore: save-book <title> <author> <year>
登録と表示
2件登録したあと、全件取得します。
shell:>save-book test1 author1 2021
Book(id=5644004762845184, title='test1', author='author1', year=2021)
shell:>save-book test2 author2 2021
Book(id=5632499082330112, title='test2', author='author2', year=2021)
shell:>find-all-books
[Book(id=5632499082330112, title='test2', author='author2', year=2021), Book(id=5644004762845184, title='test1', author='author1', year=2021)]
9. See what is stored in Datastore using web interface(登録内容の確認)
10. Clean up(削除)
削除後にコマンド及びWebから確認しましたがちゃんとデータが消えていますね。
shell:>remove-all-books
shell:>find-all-books
[]
Gradle コマンドで実行した時に不具合
Gradle コマンド使って ./gradlew bootRun
した場合、EXECUTING [16s]
の出力が常にされておりインタラクティブコマンドがうまく動かなかったです。実行は可能ですが表示がめちゃくちゃ崩れます。。。。
原因わかる方いましたら教えてください m(_ _)m
% ./gradlew bootRun
> Task :bootRun
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0-SNAPSHOT)
2021-01-16 13:56:59.649 INFO 78536 --- [ main] a.n.w.e.d.DatastoreApplicationKt : Starting DatastoreApplicationKt using Java 14.0.1 on MacBook-Pro.local with PID 78536 (/Users/yasu/Downloads/datastore/build/classes/kotlin/main started by yasu in /Users/yasu/Downloads/datastore)
2021-01-16 13:56:59.651 INFO 78536 --- [ main] a.n.w.e.d.DatastoreApplicationKt : No active profile set, falling back to default profiles: default
2021-01-16 13:57:00.140 INFO 78536 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Datastore repositories in DEFAULT mode.
2021-01-16 13:57:00.184 INFO 78536 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 39 ms. Found 1 Datastore repository interfaces.
2021-01-16 13:57:00.587 INFO 78536 --- [ main] o.s.c.g.a.c.GcpContextAutoConfiguration : The default project ID is api-test-300213
2021-01-16 13:57:00.632 INFO 78536 --- [ main] o.s.c.g.core.DefaultCredentialsProvider : Default credentials provider for service account todo-test@api-test-300213.iam.gserviceaccount.com
2021-01-16 13:57:00.632 INFO 78536 --- [ main] o.s.c.g.core.DefaultCredentialsProvider : Scopes in use by default credentials: [https://www.googleapis.com/auth/pubsub, https://www.googleapis.com/auth/spanner.admin, https://www.googleapis.com/auth/spanner.data, https://www.googleapis.com/auth/datastore, https://www.googleapis.com/auth/sqlservice.admin, https://www.googleapis.com/auth/devstorage.read_only, https://www.googleapis.com/auth/devstorage.read_write, https://www.googleapis.com/auth/cloudruntimeconfig, https://www.googleapis.com/auth/trace.append, https://www.googleapis.com/auth/cloud-platform, https://www.googleapis.com/auth/cloud-vision, https://www.googleapis.com/auth/bigquery, https://www.googleapis.com/auth/monitoring.write]
2021-01-16 13:57:02.198 WARN 78536 --- [ main] org.jline : Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)
2021-01-16 13:57:02.311 INFO 78536 --- [ main] a.n.w.e.d.DatastoreApplicationKt : Started DatastoreApplicationKt in 3.019 seconds (JVM running for 3.525)
shell:>Run
<==========---> 83% EXECUTING [16s] ← これ!!?
> :bootRun
最後に
チュートリアルを通して実践してみましたが、思ったより簡単だったんですが kotlin が初心者すぎて思った以上に時間がかかりました。
spring-boot/kotlin/datastore 使って API の方も作っていこうと思うので余力があれば Qiitaにも投稿したいと思います。
たいしたコードじゃないですがソースコードは一応 GitHub にあげておきます。
https://github.com/ynozue/DatasotreSample_kotlin