etiennestuder / gradle-jooq-plugin

Gradle plugin that integrates jOOQ.
Apache License 2.0
512 stars 84 forks source link

gradle-jooq-plugin

The work on this software project is in no way associated with my employer nor with the role I'm having at my employer. Any requests for changes will be decided upon exclusively by myself based on my personal preferences. I maintain this project as much or as little as my spare time permits.

Overview

Gradle plugin that integrates the jOOQ code generation tool.

For each named jOOQ configuration declared in the build, the plugin adds a task to generate the jOOQ sources from the specified database schema and includes the generated Java sources in the matching source set, if existing. The code generation tasks participate in task configuration avoidance, in build configuration caching, in incremental builds, in task output caching, and in toolchains. The plugin can be applied on both Java projects and Android projects.

You can find more details about the actual jOOQ source code generation in the jOOQ documentation.

The jOOQ plugin is hosted at the Gradle Plugin Portal.

Build scan

Recent build scan: https://gradle.com/s/zcltzipq7zzha

Find out more about build scans for Gradle and Maven at https://scans.gradle.com.

Functionality

The following functionality is provided by the jOOQ plugin:

The following Gradle configuration changes are contributed by the jOOQ plugin:

The following Gradle features are supported by the jOOQ plugin:

Compatibility

Plugin version Compatible Gradle versions Support for Gradle Kotlin DSL Support for Gradle Configuration Cache Minimum JDK Minimum jOOQ
9.0+ 8.0+ Yes Yes 17 3.16+
8.0+ 7.0+ Yes Yes 17 3.16+
7.0+ 6.1+, 7.0+ Yes Yes 11 3.16+
6.0+ 6.1+, 7.0+ Yes Yes 11 <= 3.15
5.0+ 6.1+, 7.0+ Yes Yes 8 <= 3.15
4.0 5.0+, 6.0+, 7.0+ No No 8 <= 3.15

See the Migration section on how to migrate your build from older to newer jOOQ plugin versions.

Configuration

Applying the plugin

Apply the nu.studer.jooq plugin to your Gradle project.

Gradle Groovy DSL

plugins {
    id 'nu.studer.jooq' version '9.0'
}

Gradle Kotlin DSL

plugins {
    id("nu.studer.jooq") version "9.0"
}

Adding the database driver

Add the database driver of the database that the jOOQ code generation tool will introspect to the jooqGenerator configuration. This ensures that the database driver is on the classpath when the jOOQ code generation tool is executed. Optionally, you can add additional dependencies that are required to run the jOOQ code generation tool.

Gradle Groovy DSL

dependencies {
    jooqGenerator 'org.postgresql:postgresql:42.5.4'
}

Gradle Kotlin DSL

dependencies {
    jooqGenerator("org.postgresql:postgresql:42.5.4")
}

Specifying the jOOQ version and edition

Specify the version and edition of the jOOQ dependencies automatically added by the plugin.

The <edition>:jooq-codegen:<version> dependency of the specified version and edition is automatically added to the jooqGenerator configuration.

The <edition>:jooq:<version> dependency of the specified version and edition is automatically added to the implementation configuration of the source set that matches the name of the declared jOOQ configuration, if any.

Gradle Groovy DSL

jooq {
  version = '3.19.1'  // the default (can be omitted)
  edition = nu.studer.gradle.jooq.JooqEdition.OSS  // the default (can be omitted)
}

Gradle Kotlin DSL

jooq {
  version.set("3.19.1")  // the default (can be omitted)
  edition.set(nu.studer.gradle.jooq.JooqEdition.OSS)  // the default (can be omitted)
}

Enforcing the jOOQ configuration XML schema version

Enforce a certain version of the jOOQ configuration XML schema that is different to the default version and edition configured by the jOOQ plugin by declaring what version of the jOOQ code generation tool to make available to the jOOQ plugin at configuration time, i.e. in the DSL of the jOOQ plugin.

Gradle Groovy DSL

buildscript {
    configurations['classpath'].resolutionStrategy.eachDependency {
        if (requested.group.startsWith('org.jooq') && requested.name.startsWith('jooq')) {
            useVersion '3.17.3'
        }
    }
}

Gradle Kotlin DSL

buildscript {
    configurations["classpath"].resolutionStrategy.eachDependency {
        if (requested.group.startsWith("org.jooq") && requested.name.startsWith("jooq")) {
            useVersion("3.17.3")
        }
    }
}

Configuring the jOOQ generation tool

Configure the jOOQ generation tool via jooq extension, made available by the jOOQ plugin. The full set of configuration options when using jOOQ 3.19.x can be seen on the jOOQ generation tool's Configuration class, or on the jOOQ XSD.

By default, the generated sources are written to <projectDir>/build/generated-src/jooq/<configurationName>. The target directory can be changed by explicitly setting the directory attribute of the target configuration of the generator configuration.

Gradle Groovy DSL

import org.jooq.meta.jaxb.Logging

jooq {
    version = '3.19.1'  // default (can be omitted)
    edition = nu.studer.gradle.jooq.JooqEdition.OSS  // default (can be omitted)

    configurations {
        main {  // name of the jOOQ configuration
            generateSchemaSourceOnCompilation = true  // default (can be omitted)

            generationTool {
                logging = Logging.WARN
                jdbc {
                    driver = 'org.postgresql.Driver'
                    url = 'jdbc:postgresql://localhost:5432/sample'
                    user = 'some_user'
                    password = 'some_secret'
                    properties {
                        property {
                            key = 'ssl'
                            value = 'true'
                        }
                    }
                }
                generator {
                    name = 'org.jooq.codegen.DefaultGenerator'
                    database {
                        name = 'org.jooq.meta.postgres.PostgresDatabase'
                        inputSchema = 'public'
                        forcedTypes {
                            forcedType {
                                name = 'varchar'
                                includeExpression = '.*'
                                includeTypes = 'JSONB?'
                            }
                            forcedType {
                                name = 'varchar'
                                includeExpression = '.*'
                                includeTypes = 'INET'
                            }
                        }
                    }
                    generate {
                        deprecated = false
                        records = true
                        immutablePojos = true
                        fluentSetters = true
                    }
                    target {
                        packageName = 'nu.studer.sample'
                        directory = 'build/generated-src/jooq/main'  // default (can be omitted)
                    }
                    strategy.name = 'org.jooq.codegen.DefaultGeneratorStrategy'
                }
            }
        }
    }
}

See the Examples section for complete, exemplary build scripts that apply the jOOQ plugin.

Gradle Kotlin DSL

import org.jooq.meta.jaxb.ForcedType
import org.jooq.meta.jaxb.Logging
import org.jooq.meta.jaxb.Property

jooq {
    version.set("3.19.1")  // default (can be omitted)
    edition.set(nu.studer.gradle.jooq.JooqEdition.OSS)  // default (can be omitted)

    configurations {
        create("main") {  // name of the jOOQ configuration
            generateSchemaSourceOnCompilation.set(true)  // default (can be omitted)

            jooqConfiguration.apply {
                logging = Logging.WARN
                jdbc.apply {
                    driver = "org.postgresql.Driver"
                    url = "jdbc:postgresql://localhost:5432/sample"
                    user = "some_user"
                    password = "some_secret"
                    properties.add(Property().apply {
                        key = "ssl"
                        value = "true"
                    })
                }
                generator.apply {
                    name = "org.jooq.codegen.DefaultGenerator"
                    database.apply {
                        name = "org.jooq.meta.postgres.PostgresDatabase"
                        inputSchema = "public"
                        forcedTypes.addAll(listOf(
                            ForcedType().apply {
                                name = "varchar"
                                includeExpression = ".*"
                                includeTypes = "JSONB?"
                            },
                            ForcedType().apply {
                                name = "varchar"
                                includeExpression = ".*"
                                includeTypes = "INET"
                            }
                        ))
                    }
                    generate.apply {
                        isDeprecated = false
                        isRecords = true
                        isImmutablePojos = true
                        isFluentSetters = true
                    }
                    target.apply {
                        packageName = "nu.studer.sample"
                        directory = "build/generated-src/jooq/main"  // default (can be omitted)
                    }
                    strategy.name = "org.jooq.codegen.DefaultGeneratorStrategy"
                }
            }
        }
    }
}

See the Examples section for complete, exemplary build scripts that apply the jOOQ plugin.

Configuring the jOOQ generation task to participate in incremental builds and build caching

If you configure the state of the database schema from which to derive the jOOQ sources as an input to the jOOQ task, you can mark the jOOQ task as having all its inputs declared by setting the allInputsDeclared task property to true. The jOOQ task will then participate in Gradle's incremental build and build caching features. The allInputsDeclared task property is false by default.

See here for a complete example on how to enable participation in incremental build and build caching.

Gradle Groovy DSL

    tasks.named('generateJooq').configure { allInputsDeclared = true }

Gradle Kotlin DSL

    tasks.named<nu.studer.gradle.jooq.JooqGenerate>("generateJooq") { allInputsDeclared.set(true) }

Configuring the jOOQ generation task with a toolchain

If you configure a toolchain on the project to which the jOOQ task belongs, it is automatically used by the jOOQ task. You can also configure / override the toolchain on the jOOQ task itself.

Gradle Groovy DSL

    tasks.named('generateJooq').configure {
        launcher = javaToolchains.launcherFor {
            languageVersion = JavaLanguageVersion.of(18)
        }
    }

See here for a complete example on how to configure the toolchain to be used by the jOOQ task, using the Gradle DSL.

Gradle Kotlin DSL

    tasks.named<nu.studer.gradle.jooq.JooqGenerate>("generateJooq") {
        (launcher::set)(javaToolchains.launcherFor {
            languageVersion.set(JavaLanguageVersion.of(18))
        })
    }

Note: (launcher::set)(...) is a necessary workaround to deal with an ambiguous overloading issue in the Kotlin compiler.

See here for a complete example on how to configure the toolchain to be used by the jOOQ task, using the Kotlin DSL.

Avoiding configuration pitfalls

Synchronizing the jOOQ version between the Spring Boot Gradle plugin and the jOOQ Gradle plugin

It is advisable that the jOOQ Gradle plugin and the Spring Boot Gradle plugin are configured to use the same version of jOOQ.

If you want the Spring Boot plugin to pull in the same version of jOOQ as defined by the jOOQ plugin, you have to explicitly set ext['jooq.version'] = jooq.version.get().

The other way around, if you want the jOOQ plugin to pull in the same version of jOOQ as defined by the Spring Boot plugin, you have to explicitly set jooq.version = dependencyManagement.importedProperties['jooq.version'].

Enforcing dependency versions via dependency rules from third-party plugins or from the build itself

If the code generation fails with exceptions about not finding certain JAXB classes, it is likely due to a 3rd-party plugin or your own build adding some dependency rules that enforce certain dependency versions that are not matching what is needed by the jOOQ code generation tool. For example, the Spring Dependency Management Gradle plugin will downgrade the jakarta.xml.bind:jakarta.xml.bind-api dependency to a version not compatible with the jOOQ code generation tool and leads to the error below. This issue provides some insights on how to debug such cases.

Exception in thread "main" java.lang.NoClassDefFoundError: jakarta/xml/bind/annotation/XmlSchema

Generating sources into shared folders, e.g. src/main/java

My recommendation is to generate the jOOQ sources into a distinct folder, e.g. src/generated/jooq or build/generated-src/jooq (default). This avoids overlapping outputs, and it also keeps the door open to let Gradle cache the generated sources which can be a significant build performance gain. The rationale is explained very well in the Build Cache User Guide.

Configuring a sequence of elements using the Gradle Groovy DSL

Resemblance of the jOOQ configuration DSL with the Groovy language is coincidental. Complex types that include sequences like ForcedTypes must be defined in the DSL's nesting style:

forcedTypes {
  forcedType {
    name = 'varchar'
    expression = '.*'
    types = 'JSONB?'
  }
  forcedType {
    name = 'varchar'
    expression = '.*'
    types = 'INET'
  }
}

The Groovy list style is not supported:

forcedTypes = [
  {
    name = 'varchar'
    expression = '.*'
    types = 'JSONB?'
  },
  {
    name = 'varchar'
    expression = '.*'
    types = 'INET'
  }
]

Working with Configurations in the Kotlin DSL

See here for additional insights on configuring the jOOQ code generation tool using the Gradle Kotlin DSL.

Execution

Generating the jOOQ sources

You can generate the jOOQ sources for a given jOOQ configuration by invoking the task generate<configName>Jooq, e.g. generateTestJooq. The only exception being main that is abbreviated to generateJooq, similarly to how it is done for the JavaCompile tasks contributed by the java plugin. The generated jOOQ sources are automatically added to the source set with the name that matches the name of the given jOOQ configuration.

./gradlew generateJooq

By default, the code generation tasks are automatically configured as dependencies of the corresponding source compilation tasks provided by the JavaBasePlugin plugin. Hence, running a build that eventually needs to compile sources will first trigger the required jOOQ code generation tasks. This auto-triggering of the code generation when compiling the containing source set can be turned off by setting generateSchemaSourceOnCompilation to false on the jOOQ configuration.

Deleting the generated jOOQ sources

You can delete the generated jOOQ sources by invoking the task rule cleanGenerate<configName>Jooq, e.g. cleanGenerateTestJooq. The only exception being main that is abbreviated to cleanGenerateJooq, similarly to how it is done for the JavaCompile tasks contributed by the java plugin. The task rule will delete all files in the folder that is configured as the destination directory, regardless of whether the files were generated by the jOOQ plugin or not.

./gradlew cleanGenerateJooq

Migration

Migrating from jOOQ plugin 8.x to 9.x

When migrating your build from jOOQ plugin 8.x to 9.x, follow these steps:

Migrating from jOOQ plugin 7.x to 8.x

When migrating your build from jOOQ plugin 7.x to 8.x, follow these steps:

Migrating from jOOQ plugin 6.x to 7.x

When migrating your build from jOOQ plugin 6.x to 7.x, follow these steps:

Migrating from jOOQ plugin 5.x to 6.x

When migrating your build from jOOQ plugin 5.x to 6.x, follow these steps:

Migrating from jOOQ plugin 4.x to 5.x

When migrating your build from jOOQ plugin 4.x to 5.x, follow these steps:

Examples

Changelog

Feedback and Contributions

Both feedback and contributions are very welcome.

Acknowledgements

License

This plugin is available under the Apache License, Version 2.0.

(c) by Etienne Studer