Skip to content

monosoul/jooq-gradle-plugin

Repository files navigation

Gradle Docker jOOQ Plugin

Build Status codecov Gradle Plugins Release Maven Central Release license Semantic Release

Copyright 2021 Adrian Skrobacz

Copyright 2021 Revolut Ltd

Copyright 2022 - 2023 Andrei Nevedomskii


Notice: this plugin was originally developed at revolut-engineering/jooq-plugin, but since I can't publish it under the same group, I had to change the group from com.revolut to dev.monosoul.


This repository contains Gradle plugin for generating jOOQ classes in dockerized databases. Plugin registers task generateJooqClasses that does following steps:

  • pulls docker image
  • starts database container
  • runs migrations using Flyway
  • generates jOOQ classes

The plugin uses testcontainers library to spin up the DB container. To avoid conflicts with other plugins using docker java library or testcontainers, the plugin shades testcontainers library and it's dependencies into dev.monosoul.jooq.shadow package.

Due to that in case you'd like to customize docker client strategy, the class names will have to be prefixed with dev.monosoul.jooq.shadow, while the property name will be dev.monosoul.jooq.docker.client.strategy instead of docker.client.strategy. E.g.:

dev.monosoul.jooq.docker.client.strategy=dev.monosoul.jooq.shadow.org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy

Also, instead of TESTCONTAINERS_DOCKER_CLIENT_STRATEGY environment variable, you should use DEV_MONOSOUL_JOOQ_TESTCONTAINERS_DOCKER_CLIENT_STRATEGY.

Examples

Detailed examples are available in the examples directory of this repository.

By default plugin is configured to work with PostgreSQL, so the following minimal config is enough:

import dev.monosoul.jooq.RecommendedVersions

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jooq:jooq:${RecommendedVersions.JOOQ_VERSION}")
    jooqCodegen("org.postgresql:postgresql:42.3.6")
}

It will look for migration files in src/main/resources/db/migration and will output generated classes to build/generated-jooq in package org.jooq.generated. All of that details can be configured on the task itself as shown in examples below.

Configuring schema names and other parameters of the task:

import dev.monosoul.jooq.RecommendedVersions

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

tasks {
    generateJooqClasses {
        schemas.set(listOf("public", "other_schema"))
        basePackageName.set("org.jooq.generated")
        migrationLocations.setFromFilesystem("src/main/resources/db/migration")
        outputDirectory.set(project.layout.buildDirectory.dir("generated-jooq"))
        flywayProperties.put("flyway.placeholderReplacement", "false")
        includeFlywayTable.set(true)
        outputSchemaToDefault.add("public")
        schemaToPackageMapping.put("public", "fancy_name")
        usingJavaConfig {
            /* "this" here is the org.jooq.meta.jaxb.Generator configure it as you please */
        }
    }
}

dependencies {
    implementation("org.jooq:jooq:${RecommendedVersions.JOOQ_VERSION}")
    jooqCodegen("org.postgresql:postgresql:42.3.6")
}

To configure the plugin to use another version or edition of Flyway the following config can be used:

import dev.monosoul.jooq.RecommendedVersions

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jooq:jooq:${RecommendedVersions.JOOQ_VERSION}")
    jooqCodegen("org.postgresql:postgresql:42.3.6")
    jooqCodegen("org.flywaydb.enterprise:flyway-core:${RecommendedVersions.FLYWAY_VERSION}")
    jooqCodegen("org.flywaydb:flyway-database-postgresql:${RecommendedVersions.FLYWAY_VERSION}")
}

To configure the plugin to use another version or edition of jOOQ the following config can be used:

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

val jooqVersion = "3.15.10"

dependencies {
    implementation("org.jooq:jooq:$jooqVersion")
    jooqCodegen("org.postgresql:postgresql:42.3.6")
    jooqCodegen("org.jooq:jooq-codegen:$jooqVersion")
}

To configure the plugin to work with another DB like MySQL the following config can be applied:

import dev.monosoul.jooq.RecommendedVersions

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

jooq {
    withContainer {
        image {
            name = "mysql:8.0.29"
            envVars = mapOf(
                "MYSQL_ROOT_PASSWORD" to "mysql",
                "MYSQL_DATABASE" to "mysql"
            )
        }

        db {
            username = "root"
            password = "mysql"
            name = "mysql"
            port = 3306

            jdbc {
                schema = "jdbc:mysql"
                driverClassName = "com.mysql.cj.jdbc.Driver"
            }
        }
    }
}

dependencies {
    implementation("org.jooq:jooq:3.16.5")
    jooqCodegen("mysql:mysql-connector-java:8.0.29")
    jooqCodegen("org.flywaydb:flyway-mysql:${RecommendedVersions.FLYWAY_VERSION}")
}

To register custom types:

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

tasks {
    generateJooqClasses {
        usingJavaConfig {
            database.withForcedTypes(
                ForcedType()
                    .withUserType("com.google.gson.JsonElement")
                    .withBinding("com.example.PostgresJSONGsonBinding")
                    .withTypes("JSONB")
            )
        }
    }
}

dependencies {
    implementation("org.jooq:jooq:3.16.5")
    jooqCodegen("org.postgresql:postgresql:42.3.6")
}

To use XML-based configuration:

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

tasks {
    generateJooqClasses {
        usingXmlConfig()
    }
}

dependencies {
    implementation("org.jooq:jooq:3.16.5")
    jooqCodegen("org.postgresql:postgresql:42.3.6")
}

by default the path to the XML config is src/main/resources/db/jooq.xml, where it's content looks as following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration xmlns="http://www.jooq.org/xsd/jooq-codegen-3.16.5.xsd">
    <generator>
        <database>
            <inputSchema>public</inputSchema>
            <includes>.*</includes>
            <forcedTypes>
                <forcedType>
                    <includeTypes>JSONB</includeTypes>
                    <userType>com.google.gson.JsonElement</userType>
                    <binding>com.example.PostgresJSONGsonBinding</binding>
                </forcedType>
            </forcedTypes>
        </database>
    </generator>
</configuration>

To customize the XML config path:

tasks {
    generateJooqClasses {
        usingXmlConfig(project.layout.projectDirectory.file("src/main/resources/db/other-jooq-config.xml"))
    }
}

To customize the loaded XML config:

tasks {
    generateJooqClasses {
        usingXmlConfig {
            database.withExcludes("BAR")
        }
    }
}

To exclude flyway schema history table from generated classes:

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

tasks {
    generateJooqClasses {
        schemas.set(listOf("other"))
        usingJavaConfig {
            database.withExcludes("flyway_schema_history")
        }
    }
}

dependencies {
    implementation("org.jooq:jooq:3.16.5")
    jooqCodegen("org.postgresql:postgresql:42.3.6")
}

Remote docker setup

If you want to use the plugin with remote docker instance, refer to the testcontainers documentation .

Remote database setup

The plugin supports remote database setup, where an external DB can be used to generate jOOQ classes instead of spinning up a container with the DB. This setup can also be convenient when a container with the DB is created externally (for example with Docker compose).

To use the plugin with a remote DB:

plugins {
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

jooq {
    withoutContainer {
        db {
            username = "postgres"
            password = "postgres"
            name = "postgres"
            host = "remotehost"
            port = 5432
        }
    }
}

dependencies {
    jooqCodegen("org.postgresql:postgresql:42.3.6")
}

Multi-database setup

The plugin supports multi-database setup, where jOOQ classes could be generated out of different RDBMS. This could be achieved by registering a separate class generation task for every RDBMS.

Here's an example how to generate jOOQ classes for PostgreSQL and MySQL in a single project:

import dev.monosoul.jooq.GenerateJooqClassesTask
import dev.monosoul.jooq.RecommendedVersions

plugins {
    kotlin("jvm") version "1.6.21"
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

tasks {
    generateJooqClasses {
        basePackageName.set("org.jooq.generated.postgres")
        migrationLocations.setFromFilesystem("src/main/resources/postgres/migration")
        outputDirectory.set(project.layout.buildDirectory.dir("postgres"))
    }

    register<GenerateJooqClassesTask>("generateJooqClassesForMySql") {
        basePackageName.set("org.jooq.generated.mysql")
        migrationLocations.setFromFilesystem("src/main/resources/mysql/migration")
        outputDirectory.set(project.layout.buildDirectory.dir("mysql"))

        withContainer {
            image {
                name = "mysql:8.0.29"
                envVars = mapOf(
                    "MYSQL_ROOT_PASSWORD" to "mysql",
                    "MYSQL_DATABASE" to "mysql"
                )
            }
            db {
                username = "root"
                password = "mysql"
                name = "mysql"
                port = 3306

                jdbc {
                    schema = "jdbc:mysql"
                    driverClassName = "com.mysql.cj.jdbc.Driver"
                }
            }
        }
    }
}

dependencies {
    implementation(kotlin("stdlib"))
    jooqCodegen("org.postgresql:postgresql:42.3.6")
    jooqCodegen("mysql:mysql-connector-java:8.0.29")
    jooqCodegen("org.flywaydb:flyway-mysql:${RecommendedVersions.FLYWAY_VERSION}")
    jooqCodegen("org.flywaydb:flyway-database-postgresql:${RecommendedVersions.FLYWAY_VERSION}")
    implementation("org.jooq:jooq:3.16.6")
}

where:

  • For PostgreSQL:
    • migrations are located in src/main/resources/postgres/migration
    • generated classes are located in build/postgres under org.jooq.generated.postgres package
  • For MySQL:
    • migrations are located in src/main/resources/mysql/migration
    • generated classes are located in build/mysql under org.jooq.generated.mysql package

Basically, the plugin has 2 sets of configurations: global (or project-wide) configuration declared within jooq {} block and local (or task-specific) configuration declared for each task separately.

Local (or task-specific) configuration initial values are inherited from the global (or project-wide) configuration. So if you modify the global configuration first, and then modify the local configuration, the local configuration's initial values will be equal to the global configuration's values.

Modifying the local configuration will not affect the global configuration.

Configuration with properties

The plugin supports configuration with properties.

Here's an example of how to use gradle.properties file to configure the plugin to generate jOOQ classes for MySQL:

gradle.properties:

dev.monosoul.jooq.withContainer.db.username=root
dev.monosoul.jooq.withContainer.db.password=mysql
dev.monosoul.jooq.withContainer.db.name=mysql
dev.monosoul.jooq.withContainer.db.port=3306
dev.monosoul.jooq.withContainer.db.jdbc.schema=jdbc:mysql
dev.monosoul.jooq.withContainer.db.jdbc.driverClassName=com.mysql.cj.jdbc.Driver
dev.monosoul.jooq.withContainer.image.name=mysql:8.0.29
dev.monosoul.jooq.withContainer.image.envVars.MYSQL_ROOT_PASSWORD=mysql
dev.monosoul.jooq.withContainer.image.envVars.MYSQL_DATABASE=mysql

build.gradle.kts:

import dev.monosoul.jooq.RecommendedVersions

plugins {
    kotlin("jvm") version "1.6.21"
    id("dev.monosoul.jooq-docker")
}

repositories {
    mavenCentral()
}

dependencies {
    jooqCodegen("mysql:mysql-connector-java:8.0.29")
    jooqCodegen("org.flywaydb:flyway-mysql:${RecommendedVersions.FLYWAY_VERSION}")
    implementation("org.jooq:jooq:${RecommendedVersions.JOOQ_VERSION}")
}

And here's an example how to customize the plugin configuration from command line:

./gradlew build -Pdev.monosoul.jooq.withContainer.db.username=root -Pdev.monosoul.jooq.withContainer.db.password=password

❗ NOTE: withoutContainer properties have higher priority than withContainer properties.

❗ NOTE: properties only affect global (or project-wide) configuration.

Java-based migrations

The plugin supports Java-based Flyway migrations.

If you have Java-based migrations as a part of your project in a Gradle submodule, you can use the following configuration:

import dev.monosoul.jooq.RecommendedVersions

plugins {
  id("dev.monosoul.jooq-docker")
}

repositories {
  mavenCentral()
}

tasks {
  generateJooqClasses {
    migrationLocations.setFromClasspath(
      project(":migrations").sourceSets.main.map { it.output },
      "package/with/migrations"
    )
  }
}

dependencies {
  implementation("org.jooq:jooq:${RecommendedVersions.JOOQ_VERSION}")
  jooqCodegen("org.postgresql:postgresql:42.3.6")
}

If you want to run migrations provided in a JAR file by some third party:

import dev.monosoul.jooq.RecommendedVersions

plugins {
  id("dev.monosoul.jooq-docker")
}

repositories {
  mavenCentral()
}

val migrationClasspath by configurations.creating

tasks {
  generateJooqClasses {
    migrationLocations.setFromClasspath(
      migrationClasspath,
      "package/with/migrations"
    )
  }
}

dependencies {
  implementation("org.jooq:jooq:${RecommendedVersions.JOOQ_VERSION}")
  migrationClasspath("third.party:library:version")
  jooqCodegen("org.postgresql:postgresql:42.3.6")
}