Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java/jOOQ: Add code generation add-on for Gradle, with reflection from SQL DDL files #11

Merged
merged 2 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 47 additions & 13 deletions by-language/java-jooq/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Java jOOQ demo application for CrateDB using PostgreSQL JDBC
############################################################


*****
About
*****
Expand All @@ -12,9 +13,8 @@ A demo application using `CrateDB`_ with `jOOQ`_ and the `PostgreSQL
JDBC driver`_.

It is intended as a basic example to demonstrate what currently works, and as a
testing rig for eventually growing a full-fledged CrateDB dialect, or at least
making the code generator work. Contributions are welcome.

testing rig for eventually growing a full-fledged CrateDB dialect.
Contributions are welcome.

Introduction
============
Expand All @@ -35,24 +35,48 @@ In some kind, jOOQ is similar to `LINQ`_, `but better <Insight into Language
Integrated Querying_>`_.


*******
Details
=======
*******

Overview
========

The code examples will demonstrate a few of the `different use cases for jOOQ`_.
That is, how to use the `jOOQ DSL API`_ based on code generated with `jOOQ's
code generator`_ to take your database schema and reverse-engineer it into a
set of Java classes, as well how to use the `Dynamic SQL API`_ by using `jOOQ
as a SQL builder without code generation`_.

The code example will demonstrate a few of the `different use cases for jOOQ`_.
That is, `Dynamic SQL`_, the `jOOQ DSL API`_, and how to use `jOOQ as a SQL
builder without code generation`_.
Schema management
=================

In many cases, the schema is defined in the form of SQL scripts, which can be
used with a `database schema migration`_ framework like `Flyway`_,
`Liquibase`_, `Bytebase`_, etc.

The `DDLDatabase - Code generation from SQL files`_ feature can be used to
effectively reflect the database schema from SQL DDL files, without needing
a database instance at all. The code provided within the ``src/generated``
directory has been generated like this.

Caveats
=======

- `jOOQ's code generator`_ takes your database schema and reverse-engineers it
into a set of Java classes. This feature currently does not work with CrateDB
yet. The code provided within the ``src/generated`` directory has not been
derived by reflecting the database schema from CrateDB.
- `jOOQ's code generator`_ currently does not work with directly connecting to
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably mention that since we are using the psql jdbc driver and we are bound to JOOQ parser, we can only use sql-standard/psql based definitions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this only works without having any CrateDB-specific schema definitions since it has to pass the jOQL parser and will then be created in a H2 memory database.

I would probably mention that since we are using the psql jdbc driver and we are bound to JOOQ parser, we can only use sql-standard/psql based definitions.

You are right. Thanks!

11:06:01 SEVERE DDLDatabase Error        : Your SQL string could not be parsed or interpreted. This may have a variety of reasons, including:
- The jOOQ parser doesn't understand your SQL
- The jOOQ DDL simulation logic (translating to H2) cannot simulate your SQL

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've improved the "Caveats" section with 15ae4cd. Thank you again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi again. I've diverted the documentation updates to GH-12 after the review.

a real CrateDB database instance and reflecting the schema from there.
Because SQL DDL statements are usually maintained in form of multiple
incremental migration scripts anyway, this is considered to be not of a too
big concern, see above. With corresponding improvements to CrateDB, this
can be made work in the future, see `issue #10 - with reflection from the
database`_.

- Most of the jOOQ examples use uppercase letters for the database, table, and
field names. CrateDB currently only handles lowercase letters.
field names. Within this setup, we have only been able to make it work using
lowercase letters.

- We have not been able to make multiple SQL DDL statements work within a
single SQL bootstrap file at ``src/main/resources/bootstrap.sql``.
Comment on lines +78 to +79
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really a caveat? Isn't defining dedicated SQL files for each table even a cleaner way?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure. I think when using a framework managing your data migrations, there is usually one file per schema-change iteration, containing multiple ALTER TABLE statements. This caveat is here as a reminder that I discovered issues in this area, and that I would like to revisit the topic on a subsequent iteration.

Copy link
Member Author

@amotl amotl Jan 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also #11 (review) below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement has been removed. Apparently, it was based on a quirk on my side, and I have not been able to observe a problem in this area again.



*****
Expand All @@ -73,16 +97,26 @@ Usage

./gradlew test

4. Generate the jOOQ sources from the main jOOQ configuration, see ``jooq.gradle``::

./gradlew generateJooq


.. _Bytebase: https://github.com/bytebase/bytebase
.. _CrateDB: https://github.com/crate/crate
.. _database schema migration: https://en.wikipedia.org/wiki/Schema_migration
.. _DDLDatabase - Code generation from SQL files: https://www.jooq.org/doc/latest/manual/code-generation/codegen-ddl/
.. _Different use cases for jOOQ: https://www.jooq.org/doc/latest/manual/getting-started/use-cases/
.. _Dynamic SQL: https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/
.. _Dynamic SQL API: https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/
.. _Flyway: https://github.com/flyway/flyway
.. _Gradle: https://gradle.org/
.. _Insight into Language Integrated Querying: https://blog.jooq.org/jooq-tuesdays-ming-yee-iu-gives-insight-into-language-integrated-querying/
.. _issue #10 - with reflection from the database: https://github.com/crate/cratedb-examples/pull/10
.. _Java 17: https://adoptium.net/temurin/releases/?version=17
.. _jOOQ: https://github.com/jOOQ/jOOQ
.. _jOOQ as a SQL builder without code generation: https://www.jooq.org/doc/latest/manual/getting-started/use-cases/jooq-as-a-sql-builder-without-codegeneration/
.. _jOOQ's code generator: https://www.jooq.org/doc/latest/manual/code-generation/
.. _jOOQ DSL API: https://www.jooq.org/doc/latest/manual/sql-building/dsl-api/
.. _LINQ: https://en.wikipedia.org/wiki/Language_Integrated_Query
.. _Liquibase: https://github.com/liquibase/liquibase
.. _PostgreSQL JDBC Driver: https://github.com/pgjdbc/pgjdbc
4 changes: 4 additions & 0 deletions by-language/java-jooq/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ repositories {

dependencies {
implementation 'org.jooq:jooq:3.17.7'
implementation 'org.jooq:jooq-meta:3.17.7'
implementation 'org.postgresql:postgresql:42.5.1'
implementation 'org.slf4j:slf4j-api:2.0.6'
implementation 'org.slf4j:slf4j-simple:2.0.6'
Expand Down Expand Up @@ -56,6 +57,9 @@ test {
dependsOn 'cleanTest'
}

// Activate jOOQ code generation add-on.
apply from: 'jooq.gradle'

idea.module.inheritOutputDirs = true
processResources.destinationDir = compileJava.destinationDir
compileJava.dependsOn processResources
119 changes: 119 additions & 0 deletions by-language/java-jooq/jooq.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* About
* =====
*
* Configure `gradle-jooq-plugin`, a Gradle plugin that integrates the jOOQ
* code generation tool. This layout manages the jOOQ configuration within
* a separate script file.
*
* Synopsis
* ========
*
* ./gradlew generateJooq
*
* Resources
* =========
*
* - https://github.com/etiennestuder/gradle-jooq-plugin#gradle-groovy-dsl-4
* - https://github.com/etiennestuder/gradle-jooq-plugin/tree/main/example/extract_script_file
* - https://github.com/etiennestuder/gradle-jooq-plugin#examples
*/

buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath 'nu.studer:gradle-jooq-plugin:8.1'
}
}

repositories {
mavenCentral()
}

apply plugin: nu.studer.gradle.jooq.JooqPlugin

dependencies {
jooqGenerator 'org.postgresql:postgresql:42.5.1'
jooqGenerator 'org.jooq:jooq-meta-extensions:3.17.7'
}

jooq {

configurations {
// Name of the jOOQ configuration.
main {

// Do not *automatically* generate code.
generateSchemaSourceOnCompilation = false

generationTool {
logging = org.jooq.meta.jaxb.Logging.WARN
generator {
name = 'org.jooq.codegen.DefaultGenerator'
strategy.name = 'org.jooq.codegen.DefaultGeneratorStrategy'
database {
name = 'org.jooq.meta.extensions.ddl.DDLDatabase'
properties {

// Specify the location of your SQL script.
// You may use ant-style file matching, e.g. /path/**/to/*.sql
//
// Where:
// - ** matches any directory subtree
// - * matches any number of characters in a directory / file name
// - ? matches a single character in a directory / file name
property {
key = 'scripts'
value = 'src/main/resources/bootstrap.sql'
}

// The sort order of the scripts within a directory, where:
//
// - semantic: sorts versions, e.g. v-3.10.0 is after v-3.9.0 (default)
// - alphanumeric: sorts strings, e.g. v-3.10.0 is before v-3.9.0
// - flyway: sorts files the same way as flyway does
// - none: doesn't sort directory contents after fetching them from the directory
property {
key = 'sort'
value = 'semantic'
}

// The default schema for unqualified objects:
//
// - public: all unqualified objects are located in the PUBLIC (upper case) schema
// - none: all unqualified objects are located in the default schema (default)
//
// This configuration can be overridden with the schema mapping feature
property {
key = 'unqualifiedSchema'
value = 'none'
Comment on lines +89 to +91
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this confuses a bit as in the examples, no schema is used. I think this works as the testdrive is passed into the connection string as the database parameter which CrateDB then uses as the default schema name. Maybe rather use testdrive here and not inside the connection string and/or use the schema also in the application?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already tried to use a real value there, but the setting apparently only accepts public or none.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also #12 (review).

}

// The default name case for unquoted objects:
//
// - as_is: unquoted object names are kept unquoted
// - upper: unquoted object names are turned into upper case (most databases)
// - lower: unquoted object names are turned into lower case (e.g. PostgreSQL)
property {
key = 'defaultNameCase'
value = 'lower'
}
}
}
generate {
deprecated = false
records = true
immutablePojos = false
fluentSetters = false
}
target {
packageName = 'io.crate.demo.jooq.model'
directory = 'src/generated/java'
}
}
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading