kotlin-dsl-jooq-plugin
A plugin that closely mirrors the de-facto (but non-official) gradle plugin for jOOQ by etiennestuder. While the gradle plugin can be used while using kotlin-dsl it can get very difficult to use it because it employs the dynamic method mechanism of groovy, which the kotlin runtime cannot use to figure out the types and most of the types have to be annotated by the developer. This plugins also adds a couple of extra features that some use cases might require.
Author Rohan Prabhu (rohan@rohanprabhu.com)
License Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
Latest Version 0.3.1
What it provides
- Configure jooq code generation against a target database.
- Task dependency management, specifically its dependencies on
compileJava
andcompileKotlin
. As opposed to the gradle plugin, this plugin also configures the dependencies for thecompileKotlin
task. - Lock version of jooq throughout the project in all configurations.
- Specify additional source directories which could be treated as an input for the code generation task (this is useful when you are using database migrations).
Similar to the gradle plugin, you can specify multiple jooq configurations and they will be mapped to different gradle tasks which will individually generate the code.
Usage
To apply the plugin, use the gradle plugin syntax:
plugins {
id("com.rohanprabhu.kotlin-dsl-jooq") version "0.3.1"
}
Once the plugin is applied, the minimum configuration required to generate sources are:
jooqGenerator {
configuration("primary", project.java.sourceSets.getByName("main")) {
configuration = jooqCodegenConfiguration {
jdbc {
username = "rohan"
password = "password"
driver = "org.postgresql.Driver"
url = "jdbc:postgresql://localhost:5432/example_database"
schema = "public"
}
generator {
target {
packageName = "com.example.jooq"
directory = "${project.buildDir}/generated/jooq/primary"
}
}
}
}
}
The code generator is run in a classpath of its own, which is specified using jooqGeneratorRuntime
. So add your JDBC dependencies (like JDBC drivers) in the jooqGeneratorRuntime
configuration in the dependencies
block:
dependencies {
jooqGeneratorRuntime("org.postgresql:postgresql:42.1")
}
This will generate a task called jooq-codegen-primary
which will generate the jooq code into the directory as specified under target
. The configuration
object is the jaxb Configuration
provided by jooq
.
If you have multiple configurations you need to generate the code for, use:
jooqGenerator {
configuration("primary", project.java.sourcesets.getByName("main")) { ... }
configuration("analytics", project.java.sourcesets.getByName("main")) { ... }
}
This will create two tasks jooq-codegen-primary
and jooq-codegen-analytics
. Do note that both of them will be a dependency of tasks compileJava
and compileKotlin
so you would rarely need to call these tasks directly.
Do note that if you are using multiple source sets for your project, you might want to select the one you intend and pass it as the second argument of the configuration(..)
function. The generated sources are added to the source set that you specify, so that they are included as a part of your compile phase.
Configuration style
There are different ways to write the configuration, the one which was shown above:
jooqGenerator {
configuration("primary", project.java.sourceSets.getByName("main")) {
configuration = jooqCodegenConfiguration {
jdbc {
username = 'rohan'
...
}
generator {
...
}
}
}
}
Since the configuration
is simply the Configuration
from org.jooq.util.jaxb
, you can construct the object any way you want:
jooqGenerator {
configuration("primary", project.java.sourceSets.getByName("main")) {
configuration = Configuration().apply {
jdbc = Jdbc().withDriver("org.postgresql.Driver")
...
}
}
}
The first example uses the DSL convenience utilities that are provided as part of this plugin and they essentially create the jaxb package components for you. If you want to re-use certain objects, you can also use:
val commonJdbc = jdbc {
username = "rohan"
password = "password"
}
configuration("primary", project.java.sourceSets.main()) {
configuration = jooqCodegenConfiguration {
jdbc = commonJdbc
generator {
...
}
}
}
configuration("analytics", project.java.sourceSets.main()) {
configuration = jooqCodegenConfiguration {
jdbc = commonJdbc
generator {
...
}
}
}
These methods are available for almost all types that are used by the org.jooq.util.jaxb.Configuration
object.
Locking the jooq version
To specify a jooq version and edition to use, specify in the top-level jooqGenerator
configuration block:
jooqGenerator {
jooqEdition = JooqEdition.OpenSource
jooqVersion = "3.10.1"
}
Once this is specified, any jooq dependency across configurations does not need the version to be specified:
dependencies {
compile("org.jooq:jooq")
}
This will automatically pick-up the version you have specified in the configuration. Do note that there is currently no way to disable this automatic version locking. Also, the name jooqGenerator
is a bit misleading to its alternative action of locking a version number throughout the code base. I intend to rename it in the next version.
Using forced types
As has been mentioned before, since they are simply objects from the jooq package, you could simply set the forced types as:
database {
forcedTypes = listOf(ForcedType().apply { .. }, ForcedType().apply { .. })
}
but keeping in lines with the kotlin-dsl and the overall gradle convention (also mirroring sort of close to what the gradle plugin offers), you could:
database {
isIncludeIndexes = true
// other database parameters here
forcedTypes {
forcedType {
userType = "com.fasterxml.jackson.databind.JsonNode"
expression = ".*"
binding = "com.example.PostgreJSONGsonBinding"
types = "JSONB?"
}
forcedType {
name = "varchar"
expression = ".*"
types = "INET"
}
}
}
NOTE Since CustomType
directive is now deprecated in jooq, no convenience functions are provided for the same, but you can still use it if you wish using:
database {
customTypes = listOf(... construct your list here ...)
}
Other generation parameters
To fine tune the code generation parameters:
generator {
database {
...
}
generate {
relations = true
deprecated = false
records = true
immutablePojos = false
...
}
}
Task management
The tasks that are mapped are automatically set as dependencies for the compileKotlin
and compileJava
tasks so you don't have to do anything else manually. The task is considered UP-TO-DATE
unless:
- The
Configuration
object has changed. - The output directory of the generator is removed/modified (for example by a clean task or a manual delete).
- The classpath as specified by
jooqGeneratorRuntime
is modified (one example would be if you add/remove dependencies to it).
In addition to this, if you are using a migration tool like flyway, you'd want the generator to run anytime your migration directory has changed. To do so, you can:
configuration("primary", project.java.sourceSets.main()) {
databaseSources {
+ "${project.projectDir}/src/main/resources/com/example/migrations"
+ "${project.projectDir}/src/main/resources/schema.sql"
}
configuration = jooqCodegenConfiguration {
...
}
..
}
Any object can be specified after the +
symbol, and the path is resovled by gradle depending on the type, as documented here. You can also directly specify the list if you wish (if it is being constructed elsewhere or through a procedure):
databaseSources = listOf( ... ) // Any list of type List<Any>
What you'd also like to do is make your migration task a dependency of the code generation task:
val `jooq-codegen-primary` by project.tasks
`jooq-codegen-primary`.dependsOn("flywayMigrate")
Do remember to use the correct name of the task depending on the configuration name you have chosen.
Configuring the generator JVM
When the generator runs, it uses a java execution spec that is provided by the gradle infrastructure. If you wish to modify the way it runs, and/or add a handler to the post-execution result, you can:
val `jooq-codegen-priamry` : JooqCodeGenerationTask by project.tasks
`jooq-codegen-primary` {
javaExecAction = Action {
jvmArgs = listOf("-Xmx2G")
}
execResultHandler = Action {
println(">> Exited with $exitValue")
}
}
Do not forget to use the correct type annotation here (JooqCodeGenerationTask
), otherwise it'll just resolve to gradle.api.tasks.Task
and you will get an error, something along the lines of javaExecAction
and execResultHandler
not being resolved.
Contact
Please file an issue for feature requests or bugs. PRs for improvements are more than welcome.