graphql-provider/Create project & Define entities
Visit https://start.spring.io/, create a Spring boot project, select Gradle project and Kotlin
- There are three entities: BookStore, Book and Author
- There is a many-to-one association from Book to BookStore: Book.store
- There is a one-to-many association from BookStore to Book: BookStore.books
- There is a many-to-many association from Book to Author: Book.authors
- There is a many-to-many association from Author to Book: Author.books
Modify the "build.gradle.kts",
-
Add google ksp into plugins {}
plugins { ... other plugins ... id("com.google.devtools.ksp") version "1.6.10-1.0.2" }
-
Add the following three dependencies under dependencies {}
dependencies { implementation("org.babyfish.graphql.provider:graphql-provider-starter-dgs:0.0.13") ksp("org.babyfish.kimmer:kimmer-ksp:0.3.1") runtimeOnly("io.r2dbc:r2dbc-h2:0.8.5.RELEASE") }
- The first dependency is graphql-provider
- The second dependency is a gradle tool required by kimmer, it is used to generate some source code files
- The third dependency is R2DBC driver of H2 database
Click the refresh button of gradle panel to let intellij down the plugin and dependencies.
Create a new package com.example.demo.model, add 4 files under it: BookStore.kt, Book.kt, Author.kt, Gender.kt
- BookStore.kt
package com.example.demo.model import org.babyfish.kimmer.sql.Entity import java.util.* interface BookStore: Entity<UUID> { val name: String val website: String? val books: List<Book> }
- Book.kt
package com.example.demo.model import org.babyfish.kimmer.sql.Entity import java.math.BigDecimal import java.util.* interface Book: Entity<UUID> { val name: String val store: BookStore? val price: BigDecimal val authors: List<Author> }
- Author.kt
package com.example.demo.model import org.babyfish.kimmer.sql.Entity import java.util.* interface Author: Entity<UUID> { val firstName: String val lastName: String val gender: Gender val books: List<Book> }
- Gender.kt
package com.example.demo.model enum class Gender { MALE, FEMALE }
In order to use the strongly typed SQL DSL to maximize the development experience, some source code needs to be generated based on the entity interfaces. Please modify "build.gradle.kts".
-
Configure arguments of google ksp
ksp { arg("kimmer.draft", "false") // α arg("kimmer.table", "true") // β arg("kimmer.table.collection-join-only-for-sub-query", "true") // γ }
- α: Do not generate the source code required by kimmer, which is not required for this example
- β: Generate the source code required by kimmer-sql, which is required for this example
- γ: Prohibit the use of collection joins in top-level queries. This switch allows developers to develop better programming habits, click here for more information
-
Add the generated code to the gradle build path (important but easy to forget)
kotlin { sourceSets.main { kotlin.srcDir("build/generated/ksp/main/kotlin") } }
Click the refresh button of gradle panel to let ksp generate source codes.
-
Automatically initialize the H2 database when the app starts
Create a file data.sql under src/main/resouces. Due to the large amount of sql code, the sql code is not listed here and you can copy the sql code from example/src/main/resources/data.sql.
Change class com.example.demo.DempApplciation, add a spring bean
@Bean fun connectionFactoryInitializer( connectionFactory: ConnectionFactory ): ConnectionFactoryInitializer = ConnectionFactoryInitializer().apply { setConnectionFactory(connectionFactory) setDatabasePopulator(ResourceDatabasePopulator(ClassPathResource("data.sql"))) afterPropertiesSet() }
-
Let kimmer-sql use dialect of H2 database
Change class com.example.demo.DempApplciation, add a spring bean
@Bean fun dialect() = org.babyfish.kimmer.sql.runtime.dialect.H2Dialect()
-
Tell kimmer-sql how to map the enum com.example.demo.model.Gender
Change class com.example.demo.DempApplciation, add a spring bean
@Bean fun genderProvider() = enumProviderByString(Gender::class) { map(Gender.MALE, "M") map(Gender.FEMALE, "F") }
kimmer-sql provides two ways to map enum
-
enumProviderByString
This means to map the enum to string
- By default the name of the enum is used
enumProviderByString(Gender::class) // MALE, FEMALE
- You can override the default behavior with custom strings
enumProviderByString(Gender::class) { // M, F map(Gender.MALE, "M") map(Gender.FEMALE, "F") }
-
enumProviderByInt
This means to map the enum to integer
- By default the ordinal of the enum is used
enumProviderByInt(Gender::class) // 0, 1
- You can override the default behavior with custom integers
enumProviderByInt(Gender::class) { // 100, 200 map(Gender.MALE, 100) map(Gender.FEMALE, 200) }
-