Repository: jhipster/jhipster-lite
Pull request #11107 (2024-10-12 23:04:44)

Add jooq module with maven
ci test not relevant because need a database up to install with this jooq plugin, cannot test on mssql. Error to connect due to url error. 

pascalgrimaud: for the CI test, you can create a new dedicated config with what you need to test jooq

Another idea would be to add it to mariadb, it should work: https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/generate.sh#L232-L239

pascalgrimaud: for the CI test, you can create a new dedicated config with what you need to test jooq

Another idea would be to add it to mariadb, it should work: https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/generate.sh#L232-L239

fabienpuissant: > for the CI test, you can create a new dedicated config with what you need to test jooq
> 
> Another idea would be to add it to mariadb, it should work: https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/generate.sh#L232-L239

But the plugin connected to a database to introspect schema and generate classes depending on what tables exists.
Is it possible in the test ci to have a docker container up with a database ready to be connected to ?
pascalgrimaud: Here some steps in the GitHub CI:
- we launch the containers here:
  - https://github.com/jhipster/jhipster-lite/blob/main/.github/workflows/github-actions.yml#L484
  -  it will use this [start_docker_compose.sh script](https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/start_docker_compose.sh) which will launch the appropriate docker-compose file
- after that, we'll start the application here:
  - https://github.com/jhipster/jhipster-lite/blob/main/.github/workflows/github-actions.yml#L490
  - it will at least test the health management. See https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/start.sh#L36
fabienpuissant: > Here some steps in the GitHub CI:
> 
> * we launch the containers here:
>   
>   * https://github.com/jhipster/jhipster-lite/blob/main/.github/workflows/github-actions.yml#L484
>   * it will use this [start_docker_compose.sh script](https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/start_docker_compose.sh) which will launch the appropriate docker-compose file
> * after that, we'll start the application here:
>   
>   * https://github.com/jhipster/jhipster-lite/blob/main/.github/workflows/github-actions.yml#L490
>   * it will at least test the health management. See https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/start.sh#L36

I started to investigate on it but in fact the db needs to be up at compile time not when we start the app, so on step "Test: verify: " Regarding the workflow, I don't think we have this use case and I don't find a proper way to do it

Here an example : https://github.com/fabienpuissant/jhipster-lite/actions/runs/11622943610/job/32369374084
murdos: LGTM. @fabienpuissant : could you tease the branch to fix conflicts?

Patch:
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/application/JooqApplicationService.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/application/JooqApplicationService.java
new file mode 100644
index 000000000..05c7455ad
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/application/JooqApplicationService.java
@@ -0,0 +1,33 @@
+package tech.jhipster.lite.generator.server.springboot.database.jooq.application;
+
+import org.springframework.stereotype.Service;
+import tech.jhipster.lite.generator.server.springboot.database.jooq.domain.JooqModuleFactory;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.docker.DockerImages;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+@Service
+public class JooqApplicationService {
+
+  private final JooqModuleFactory factory;
+
+  public JooqApplicationService(DockerImages dockerImages) {
+    factory = new JooqModuleFactory(dockerImages);
+  }
+
+  public JHipsterModule buildPostgresql(JHipsterModuleProperties properties) {
+    return factory.buildPostgresql(properties);
+  }
+
+  public JHipsterModule buildMariaDB(JHipsterModuleProperties properties) {
+    return factory.buildMariaDB(properties);
+  }
+
+  public JHipsterModule buildMsSQL(JHipsterModuleProperties properties) {
+    return factory.buildMsSQL(properties);
+  }
+
+  public JHipsterModule buildMySQL(JHipsterModuleProperties properties) {
+    return factory.buildMySQL(properties);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/CommonModuleBuilder.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/CommonModuleBuilder.java
new file mode 100644
index 000000000..a6fbf7f82
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/CommonModuleBuilder.java
@@ -0,0 +1,89 @@
+package tech.jhipster.lite.generator.server.springboot.database.jooq.domain;
+
+import static tech.jhipster.lite.module.domain.JHipsterModule.*;
+
+import tech.jhipster.lite.module.domain.DocumentationTitle;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.docker.DockerImageVersion;
+import tech.jhipster.lite.module.domain.file.JHipsterSource;
+import tech.jhipster.lite.module.domain.javabuild.ArtifactId;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope;
+import tech.jhipster.lite.module.domain.javaproperties.PropertyValue;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+import tech.jhipster.lite.shared.error.domain.Assert;
+
+final class CommonModuleBuilder {
+
+  private static final PropertyValue FALSE = propertyValue(false);
+
+  private CommonModuleBuilder() {}
+
+  public static JHipsterModule.JHipsterModuleBuilder commonModuleBuilder(
+    JHipsterModuleProperties properties,
+    DatabaseType databaseType,
+    DockerImageVersion dockerImage,
+    DocumentationTitle documentationTitle,
+    ArtifactId testContainerArtifactId
+  ) {
+    Assert.notNull("properties", properties);
+    Assert.notNull("databaseType", databaseType);
+    Assert.notNull("dockerImage", dockerImage);
+    Assert.notNull("documentationTitle", documentationTitle);
+    Assert.notNull("testContainerArtifactId", testContainerArtifactId);
+
+    String databaseId = databaseType.id();
+    JHipsterSource source = from("server/springboot/database/common");
+
+    //@formatter:off
+    return moduleBuilder(properties)
+      .context()
+      .put("srcMainDocker", "src/main/docker") // To be used in <databaseId>>.md file
+      .put("databaseType", databaseId)
+      .put(databaseId + "DockerImageWithVersion", dockerImage.fullName()) // To be used in <databaseId>.yml docker-compose file
+      .and()
+      .documentation(documentationTitle, source.template("databaseType.md"))
+      .startupCommands()
+      .dockerCompose(startupCommand(databaseId))
+      .and()
+      .files()
+      .add(source.append("docker").template(databaseId + ".yml"), toSrcMainDocker().append(databaseId + ".yml"))
+      .and()
+      .javaDependencies()
+      .addDependency(groupId("org.springframework.boot"), artifactId("spring-boot-starter-jooq"))
+      .addDependency(groupId("com.zaxxer"), artifactId("HikariCP"))
+      .addDependency(testContainer(testContainerArtifactId))
+      .and()
+      .springMainProperties()
+      .set(propertyKey("spring.datasource.password"), propertyValue(""))
+      .set(propertyKey("spring.datasource.type"), propertyValue("com.zaxxer.hikari.HikariDataSource"))
+      .set(propertyKey("spring.datasource.hikari.poolName"), propertyValue("Hikari"))
+      .set(propertyKey("spring.datasource.hikari.auto-commit"), FALSE)
+      .and()
+      .springTestProperties()
+      .set(
+        propertyKey("spring.datasource.url"),
+        propertyValue("jdbc:tc:" + dockerImage.fullName() + ":///" + properties.projectBaseName().name())
+      )
+      .set(propertyKey("spring.datasource.username"), propertyValue(properties.projectBaseName().name()))
+      .set(propertyKey("spring.datasource.password"), propertyValue(""))
+      .set(propertyKey("spring.datasource.driver-class-name"), propertyValue("org.testcontainers.jdbc.ContainerDatabaseDriver"))
+      .set(propertyKey("spring.datasource.hikari.maximum-pool-size"), propertyValue(2))
+      .and();
+    //@formatter:on
+  }
+
+  private static String startupCommand(String databaseId) {
+    return "src/main/docker/" + databaseId + ".yml";
+  }
+
+  private static JavaDependency testContainer(ArtifactId testContainerArtifactId) {
+    return javaDependency()
+      .groupId("org.testcontainers")
+      .artifactId(testContainerArtifactId)
+      .dependencySlug("%s-%s".formatted("testcontainers", testContainerArtifactId))
+      .versionSlug("testcontainers")
+      .scope(JavaDependencyScope.TEST)
+      .build();
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/DatabaseType.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/DatabaseType.java
new file mode 100644
index 000000000..8feebc291
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/DatabaseType.java
@@ -0,0 +1,18 @@
+package tech.jhipster.lite.generator.server.springboot.database.jooq.domain;
+
+enum DatabaseType {
+  POSTGRESQL("postgresql"),
+  MYSQL("mysql"),
+  MARIADB("mariadb"),
+  MSSQL("mssql");
+
+  private final String id;
+
+  DatabaseType(String id) {
+    this.id = id;
+  }
+
+  public String id() {
+    return id;
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/JooqModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/JooqModuleFactory.java
new file mode 100644
index 000000000..e5c4cd550
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/JooqModuleFactory.java
@@ -0,0 +1,238 @@
+package tech.jhipster.lite.generator.server.springboot.database.jooq.domain;
+
+import static tech.jhipster.lite.generator.server.springboot.database.jooq.domain.CommonModuleBuilder.commonModuleBuilder;
+import static tech.jhipster.lite.module.domain.JHipsterModule.*;
+import static tech.jhipster.lite.module.domain.JHipsterModule.lineBeforeText;
+
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.LogLevel;
+import tech.jhipster.lite.module.domain.docker.DockerImageVersion;
+import tech.jhipster.lite.module.domain.docker.DockerImages;
+import tech.jhipster.lite.module.domain.file.JHipsterSource;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope;
+import tech.jhipster.lite.module.domain.mavenplugin.MavenBuildPhase;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+public class JooqModuleFactory {
+
+  public static final String GENERATE = "generate";
+  public static final String JOOQ_CODEGEN = "jooq-codegen";
+  public static final String JOOQ_CODEGEN_MAVEN = "jooq-codegen-maven";
+  public static final String ORG_JOOQ = "org.jooq";
+  public static final String MSSQL_PASSWORD = "yourStrong(!)Password";
+  public static final String ORG_POSTGRESQL = "org.postgresql";
+  private static final String MYSQL = "mysql";
+  private static final String MYSQL_GROUP_ID = "com.mysql";
+  private static final String MYSQL_ARTIFACT_ID = "mysql-connector-j";
+
+  private static final String SPRING_DATASOURCE_URL = "spring.datasource.url";
+  private static final String SPRING_DATASOURCE_USERNAME = "spring.datasource.username";
+  private static final String SPRING_DATASOURCE_DRIVER_CLASS_NAME = "spring.datasource.driver-class-name";
+
+  private final DockerImages dockerImages;
+
+  public JooqModuleFactory(DockerImages dockerImages) {
+    this.dockerImages = dockerImages;
+  }
+
+  public JHipsterModule buildPostgresql(JHipsterModuleProperties properties) {
+    DockerImageVersion dockerImage = dockerImages.get("postgres");
+
+    //@formatter:off
+    return commonModuleBuilder(
+      properties,
+      DatabaseType.POSTGRESQL,
+      dockerImage,
+      documentationTitle("Postgresql"),
+      artifactId("postgresql")
+    )
+      .javaDependencies()
+      .addDependency(
+        JavaDependency.builder()
+          .groupId(groupId(ORG_POSTGRESQL))
+          .artifactId(artifactId("postgresql"))
+          .scope(JavaDependencyScope.RUNTIME)
+          .build()
+      )
+      .and()
+      .mavenPlugins()
+        .plugin(mavenPlugin()
+          .groupId(ORG_JOOQ)
+          .artifactId(JOOQ_CODEGEN_MAVEN)
+          .versionSlug("jooq")
+          .addExecution(pluginExecution().goals(GENERATE).id(JOOQ_CODEGEN).phase(MavenBuildPhase.GENERATE_RESOURCES))
+          .configuration(jooqModuleCodegenConfiguration()
+            .database(tech.jhipster.lite.module.domain.jooqplugin.DatabaseType.POSTGRESQL)
+            .databaseUrl("jdbc:postgresql://localhost:5432/" + properties.projectBaseName().name())
+            .user(properties.projectBaseName().name())
+            .inputSchema("public")
+            .build()
+            .getConfiguration())
+          .build())
+      .and()
+      .springMainProperties()
+      .set(propertyKey(SPRING_DATASOURCE_URL), propertyValue("jdbc:postgresql://localhost:5432/" + properties.projectBaseName().name()))
+      .set(propertyKey(SPRING_DATASOURCE_USERNAME), propertyValue(properties.projectBaseName().name()))
+      .set(propertyKey(SPRING_DATASOURCE_DRIVER_CLASS_NAME), propertyValue("org.postgresql.Driver"))
+      .and()
+      .springTestProperties()
+      .set(
+        propertyKey(SPRING_DATASOURCE_URL),
+        propertyValue(
+          "jdbc:tc:postgresql:" + dockerImage.version().get() + ":///" + properties.projectBaseName().name() + "?TC_TMPFS=/testtmpfs:rw"
+        )
+      )
+      .and()
+      .springMainLogger(ORG_POSTGRESQL, LogLevel.WARN)
+      .springTestLogger(ORG_POSTGRESQL, LogLevel.WARN)
+      .springTestLogger("org.jboss.logging", LogLevel.WARN)
+      .build();
+    //@formatter:on
+  }
+
+  public JHipsterModule buildMariaDB(JHipsterModuleProperties properties) {
+    //@formatter:off
+    return commonModuleBuilder(
+      properties,
+      DatabaseType.MARIADB,
+      dockerImages.get("mariadb"),
+      documentationTitle("MariaDB"),
+      artifactId("mariadb")
+    )
+      .javaDependencies()
+      .addDependency(
+        javaDependency().groupId("org.mariadb.jdbc").artifactId("mariadb-java-client").scope(JavaDependencyScope.RUNTIME).build()
+      )
+      .and()
+      .mavenPlugins()
+      .plugin(mavenPlugin()
+        .groupId(ORG_JOOQ)
+        .artifactId(JOOQ_CODEGEN_MAVEN)
+        .versionSlug("jooq")
+        .addExecution(pluginExecution().goals(GENERATE).id(JOOQ_CODEGEN).phase(MavenBuildPhase.GENERATE_RESOURCES))
+        .configuration(jooqModuleCodegenConfiguration()
+          .database(tech.jhipster.lite.module.domain.jooqplugin.DatabaseType.MARIADB)
+          .databaseUrl("jdbc:mariadb://localhost:3306/" + properties.projectBaseName().name())
+            .user("root")
+            .inputSchema("properties.projectBaseName().name()")
+            .build()
+            .getConfiguration())
+        .build())
+      .and()
+      .springMainProperties()
+      .set(propertyKey(SPRING_DATASOURCE_URL), propertyValue("jdbc:mariadb://localhost:3306/" + properties.projectBaseName().name()))
+      .set(propertyKey(SPRING_DATASOURCE_USERNAME), propertyValue("root"))
+      .set(propertyKey(SPRING_DATASOURCE_DRIVER_CLASS_NAME), propertyValue("org.mariadb.jdbc.Driver"))
+      .and()
+      .build();
+    //@formatter:on
+  }
+
+  public JHipsterModule buildMySQL(JHipsterModuleProperties properties) {
+    //@formatter:off
+    return commonModuleBuilder(
+      properties,
+      DatabaseType.MYSQL,
+      dockerImages.get(MYSQL),
+      documentationTitle("MySQL"),
+      artifactId(MYSQL)
+    )
+      .javaDependencies()
+      .addDependency(javaDependency().groupId(MYSQL_GROUP_ID).artifactId(MYSQL_ARTIFACT_ID).scope(JavaDependencyScope.RUNTIME).build())
+      .and()
+      .mavenPlugins()
+      .plugin(mavenPlugin()
+        .groupId(ORG_JOOQ)
+        .artifactId(JOOQ_CODEGEN_MAVEN)
+        .versionSlug("jooq")
+        .addExecution(pluginExecution().goals(GENERATE).id(JOOQ_CODEGEN).phase(MavenBuildPhase.GENERATE_RESOURCES))
+        .configuration(jooqModuleCodegenConfiguration()
+          .database(tech.jhipster.lite.module.domain.jooqplugin.DatabaseType.MYSQL)
+          .databaseUrl("jdbc:mysql://localhost:3306/" + properties.projectBaseName().name())
+          .user("root")
+          .inputSchema("properties.projectBaseName().name()")
+          .build()
+          .getConfiguration())
+        .build())
+      .and()
+      .springMainProperties()
+      .set(propertyKey(SPRING_DATASOURCE_URL), propertyValue("jdbc:mysql://localhost:3306/" + properties.projectBaseName().name()))
+      .set(propertyKey(SPRING_DATASOURCE_USERNAME), propertyValue("root"))
+      .set(propertyKey(SPRING_DATASOURCE_DRIVER_CLASS_NAME), propertyValue("com.mysql.cj.jdbc.Driver"))
+      .and()
+      .build();
+    //@formatter:on
+  }
+
+  public JHipsterModule buildMsSQL(JHipsterModuleProperties properties) {
+    DockerImageVersion dockerImage = dockerImages.get("mcr.microsoft.com/mssql/server");
+    JHipsterSource source = from("server/springboot/database/common");
+
+    //@formatter:off
+    return commonModuleBuilder(
+      properties,
+      DatabaseType.MSSQL,
+      dockerImage,
+      documentationTitle("MsSQL"),
+      artifactId("mssqlserver")
+    )
+      .files()
+      .add(source.append("docker").template("container-license-acceptance.txt"), to("src/test/resources/container-license-acceptance.txt"))
+      .add(
+        source.template("MsSQLTestContainerExtension.java"),
+        toSrcTestJava().append(properties.basePackage().path()).append("MsSQLTestContainerExtension.java")
+      )
+      .and()
+      .javaDependencies()
+      .addDependency(javaDependency().groupId("com.microsoft.sqlserver").artifactId("mssql-jdbc").scope(JavaDependencyScope.RUNTIME).build())
+      .and()
+      .mavenPlugins()
+      .plugin(mavenPlugin()
+        .groupId(ORG_JOOQ)
+        .artifactId(JOOQ_CODEGEN_MAVEN)
+        .versionSlug("jooq")
+        .addExecution(pluginExecution().goals(GENERATE).id(JOOQ_CODEGEN).phase(MavenBuildPhase.GENERATE_RESOURCES))
+        .configuration(jooqModuleCodegenConfiguration()
+          .database(tech.jhipster.lite.module.domain.jooqplugin.DatabaseType.MSSQL)
+          .databaseUrl("jdbc:sqlserver://localhost:1433;database=" + properties.projectBaseName().name() + ";trustServerCertificate=true")
+          .user("SA")
+          .inputSchema("model")
+          .password(MSSQL_PASSWORD)
+          .getConfiguration())
+        .build())
+      .and()
+      .springMainProperties()
+      .set(
+        propertyKey(SPRING_DATASOURCE_URL),
+        propertyValue("jdbc:sqlserver://localhost:1433;database=" + properties.projectBaseName().name() + ";trustServerCertificate=true")
+      )
+      .set(propertyKey(SPRING_DATASOURCE_USERNAME), propertyValue("SA"))
+      .set(propertyKey("spring.datasource.password"), propertyValue(MSSQL_PASSWORD))
+      .set(propertyKey(SPRING_DATASOURCE_DRIVER_CLASS_NAME), propertyValue("com.microsoft.sqlserver.jdbc.SQLServerDriver"))
+      .and()
+      .springTestProperties()
+      .set(
+        propertyKey(SPRING_DATASOURCE_URL),
+        propertyValue(
+          "jdbc:tc:sqlserver:///;database=" + properties.projectBaseName().name() + ";trustServerCertificate=true?TC_TMPFS=/testtmpfs:rw"
+        )
+      )
+      .set(propertyKey(SPRING_DATASOURCE_USERNAME), propertyValue("SA"))
+      .set(propertyKey("spring.datasource.password"), propertyValue(MSSQL_PASSWORD))
+      .and()
+      .mandatoryReplacements()
+      .in(path("src/test/java").append(properties.basePackage().path()).append("IntegrationTest.java"))
+      .add(
+        lineBeforeText("import org.springframework.boot.test.context.SpringBootTest;"),
+        "import org.junit.jupiter.api.extension.ExtendWith;"
+      )
+      .add(lineBeforeText("public @interface"), "@ExtendWith(MsSQLTestContainerExtension.class)")
+      .and()
+      .and()
+      .springMainLogger("com.microsoft.sqlserver", LogLevel.WARN)
+      .springMainLogger("org.reflections", LogLevel.WARN)
+      .build();
+    //@formatter:on
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/infrastructure/primary/JooqModuleConfiguration.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/infrastructure/primary/JooqModuleConfiguration.java
new file mode 100644
index 000000000..b21ddcec1
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/infrastructure/primary/JooqModuleConfiguration.java
@@ -0,0 +1,82 @@
+package tech.jhipster.lite.generator.server.springboot.database.jooq.infrastructure.primary;
+
+import static tech.jhipster.lite.shared.slug.domain.JHLiteFeatureSlug.JOOQ;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.JOOQ_MARIADB;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.JOOQ_MSSQL;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.JOOQ_MYSQL;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.JOOQ_POSTGRESQL;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.SPRING_BOOT;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import tech.jhipster.lite.generator.server.springboot.database.jooq.application.JooqApplicationService;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleOrganization;
+import tech.jhipster.lite.module.domain.resource.JHipsterModulePropertiesDefinition;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleResource;
+
+@Configuration
+class JooqModuleConfiguration {
+
+  private static final String API_DOC_GROUP = "Spring Boot - Database";
+
+  @Bean
+  JHipsterModuleResource jooqPostgresqlModule(JooqApplicationService postgresql) {
+    return JHipsterModuleResource.builder()
+      .slug(JOOQ_POSTGRESQL)
+      .propertiesDefinition(properties())
+      .apiDoc(API_DOC_GROUP, "Add Jooq with Postgresql to project")
+      .organization(organization())
+      .tags(tags())
+      .factory(postgresql::buildPostgresql);
+  }
+
+  @Bean
+  JHipsterModuleResource jooqMariaDBModule(JooqApplicationService applicationService) {
+    return JHipsterModuleResource.builder()
+      .slug(JOOQ_MARIADB)
+      .propertiesDefinition(properties())
+      .apiDoc(API_DOC_GROUP, "Add Jooq with MariaDB to project")
+      .organization(organization())
+      .tags(tags())
+      .factory(applicationService::buildMariaDB);
+  }
+
+  @Bean
+  JHipsterModuleResource jooqMySQLModule(JooqApplicationService applicationService) {
+    return JHipsterModuleResource.builder()
+      .slug(JOOQ_MYSQL)
+      .propertiesDefinition(properties())
+      .apiDoc(API_DOC_GROUP, "Add Jooq with MySQL to project")
+      .organization(organization())
+      .tags(tags())
+      .factory(applicationService::buildMySQL);
+  }
+
+  @Bean
+  JHipsterModuleResource jooqMsSQLModule(JooqApplicationService applicationService) {
+    return JHipsterModuleResource.builder()
+      .slug(JOOQ_MSSQL)
+      .propertiesDefinition(properties())
+      .apiDoc(API_DOC_GROUP, "Add Jooq with MsSQL to project")
+      .organization(organization())
+      .tags(tags())
+      .factory(applicationService::buildMsSQL);
+  }
+
+  private static JHipsterModulePropertiesDefinition properties() {
+    return JHipsterModulePropertiesDefinition.builder()
+      .addBasePackage()
+      .addIndentation()
+      .addProjectBaseName()
+      .addSpringConfigurationFormat()
+      .build();
+  }
+
+  private static JHipsterModuleOrganization organization() {
+    return JHipsterModuleOrganization.builder().feature(JOOQ).addDependency(SPRING_BOOT).build();
+  }
+
+  private static String[] tags() {
+    return new String[] { "server", "spring", "spring-boot", "database" };
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/package-info.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/package-info.java
new file mode 100644
index 000000000..c8ee34bf3
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jooq/package-info.java
@@ -0,0 +1,2 @@
+@tech.jhipster.lite.BusinessContext
+package tech.jhipster.lite.generator.server.springboot.database.jooq;
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/JpaModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/JpaModuleFactory.java
index edd00c21f..ced259dbc 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/JpaModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/JpaModuleFactory.java
@@ -119,7 +119,7 @@ public class JpaModuleFactory {
 
   public JHipsterModule buildMsSQL(JHipsterModuleProperties properties) {
     DockerImageVersion dockerImage = dockerImages.get("mcr.microsoft.com/mssql/server");
-    JHipsterSource source = from("server/springboot/database/jpa");
+    JHipsterSource source = from("server/springboot/database/common");
 
     //@formatter:off
     return sqlCommonModuleBuilder(properties, DatabaseType.MSSQL, dockerImage, documentationTitle("MsSQL"), artifactId("mssqlserver"))
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/SQLCommonModuleBuilder.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/SQLCommonModuleBuilder.java
index 32abfa236..057f43a31 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/SQLCommonModuleBuilder.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/domain/SQLCommonModuleBuilder.java
@@ -45,7 +45,8 @@ final class SQLCommonModuleBuilder {
     Assert.notNull("testContainerArtifactId", testContainerArtifactId);
 
     String databaseId = databaseType.id();
-    JHipsterSource source = from("server/springboot/database/jpa");
+    JHipsterSource commonSource = from("server/springboot/database/common");
+    JHipsterSource jpaSource = from("server/springboot/database/jpa");
     JHipsterDestination mainDestination = toSrcMainJava()
       .append(properties.packagePath())
       .append("wire")
@@ -59,13 +60,13 @@ final class SQLCommonModuleBuilder {
         .put("databaseType", databaseId)
         .put(databaseId + "DockerImageWithVersion", dockerImage.fullName()) // To be used in <databaseId>.yml docker-compose file
         .and()
-      .documentation(documentationTitle, source.template("databaseType.md"))
+      .documentation(documentationTitle, commonSource.template("databaseType.md"))
       .startupCommands()
         .dockerCompose(startupCommand(databaseId))
         .and()
       .files()
-        .add(source.template("DatabaseConfiguration.java"), mainDestination.append("DatabaseConfiguration.java"))
-        .add(source.append("docker").template(databaseId + ".yml"), toSrcMainDocker().append(databaseId + ".yml"))
+        .add(jpaSource.template("DatabaseConfiguration.java"), mainDestination.append("DatabaseConfiguration.java"))
+        .add(commonSource.append("docker").template(databaseId + ".yml"), toSrcMainDocker().append(databaseId + ".yml"))
         .and()
       .javaDependencies()
         .addDependency(groupId("org.springframework.boot"), artifactId("spring-boot-starter-data-jpa"))
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/infrastructure/primary/JpaModuleConfiguration.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/infrastructure/primary/JpaModuleConfiguration.java
index d0ed49b5c..ebaf035fd 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/infrastructure/primary/JpaModuleConfiguration.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/jpa/infrastructure/primary/JpaModuleConfiguration.java
@@ -20,7 +20,7 @@ class JpaModuleConfiguration {
   private static final String API_DOC_GROUP = "Spring Boot - Database";
 
   @Bean
-  JHipsterModuleResource postgresqlModule(JpaApplicationService postgresql) {
+  JHipsterModuleResource jpaPostgresqlModule(JpaApplicationService postgresql) {
     return JHipsterModuleResource.builder()
       .slug(JPA_POSTGRESQL)
       .propertiesDefinition(properties())
@@ -31,7 +31,7 @@ class JpaModuleConfiguration {
   }
 
   @Bean
-  JHipsterModuleResource mariaDBModule(JpaApplicationService applicationService) {
+  JHipsterModuleResource jpaMariaDBModule(JpaApplicationService applicationService) {
     return JHipsterModuleResource.builder()
       .slug(JPA_MARIADB)
       .propertiesDefinition(properties())
@@ -42,7 +42,7 @@ class JpaModuleConfiguration {
   }
 
   @Bean
-  JHipsterModuleResource mySQLModule(JpaApplicationService applicationService) {
+  JHipsterModuleResource jpaMmySQLModule(JpaApplicationService applicationService) {
     return JHipsterModuleResource.builder()
       .slug(JPA_MYSQL)
       .propertiesDefinition(properties())
@@ -53,7 +53,7 @@ class JpaModuleConfiguration {
   }
 
   @Bean
-  JHipsterModuleResource msSQLModule(JpaApplicationService applicationService) {
+  JHipsterModuleResource jpaMsSQLModule(JpaApplicationService applicationService) {
     return JHipsterModuleResource.builder()
       .slug(JPA_MSSQL)
       .propertiesDefinition(properties())
diff --git a/src/main/java/tech/jhipster/lite/module/domain/JHipsterModule.java b/src/main/java/tech/jhipster/lite/module/domain/JHipsterModule.java
index 868add69b..0d4f881c6 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/JHipsterModule.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/JHipsterModule.java
@@ -37,6 +37,7 @@ import tech.jhipster.lite.module.domain.javadependency.JavaDependency.JavaDepend
 import tech.jhipster.lite.module.domain.javaproperties.*;
 import tech.jhipster.lite.module.domain.javaproperties.JHipsterModuleSpringFactories.JHipsterModuleSpringFactoriesBuilder;
 import tech.jhipster.lite.module.domain.javaproperties.JHipsterModuleSpringProperties.JHipsterModuleSpringPropertiesBuilder;
+import tech.jhipster.lite.module.domain.jooqplugin.JooqModuleCodegenConfiguration;
 import tech.jhipster.lite.module.domain.mavenplugin.*;
 import tech.jhipster.lite.module.domain.mavenplugin.JHipsterModuleMavenPlugins.JHipsterModuleMavenPluginsBuilder;
 import tech.jhipster.lite.module.domain.mavenplugin.MavenPlugin.MavenPluginGroupIdBuilder;
@@ -190,6 +191,10 @@ public final class JHipsterModule {
     return JavaDependency.builder();
   }
 
+  public static JooqModuleCodegenConfiguration.JooqModuleCodegenConfigurationBuilder jooqModuleCodegenConfiguration() {
+    return JooqModuleCodegenConfiguration.builder();
+  }
+
   public static MavenBuildExtensionGroupIdBuilder mavenBuildExtension() {
     return MavenBuildExtension.builder();
   }
diff --git a/src/main/java/tech/jhipster/lite/module/domain/jooqplugin/DatabaseType.java b/src/main/java/tech/jhipster/lite/module/domain/jooqplugin/DatabaseType.java
new file mode 100644
index 000000000..1484212f2
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/module/domain/jooqplugin/DatabaseType.java
@@ -0,0 +1,10 @@
+package tech.jhipster.lite.module.domain.jooqplugin;
+
+//shared enum ??
+
+public enum DatabaseType {
+  POSTGRESQL,
+  MYSQL,
+  MARIADB,
+  MSSQL,
+}
diff --git a/src/main/java/tech/jhipster/lite/module/domain/jooqplugin/JooqModuleCodegenConfiguration.java b/src/main/java/tech/jhipster/lite/module/domain/jooqplugin/JooqModuleCodegenConfiguration.java
new file mode 100644
index 000000000..bc18291c9
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/module/domain/jooqplugin/JooqModuleCodegenConfiguration.java
@@ -0,0 +1,140 @@
+package tech.jhipster.lite.module.domain.jooqplugin;
+
+import tech.jhipster.lite.shared.error.domain.Assert;
+
+public final class JooqModuleCodegenConfiguration {
+
+  private final DatabaseType database;
+  private final String databaseUrl;
+  private final String user;
+  private final String inputSchema;
+  private final String password;
+
+  private JooqModuleCodegenConfiguration(JooqModuleCodegenConfigurationBuilder builder) {
+    Assert.notNull("database", builder.database);
+    Assert.notNull("databaseUrl", builder.databaseUrl);
+    Assert.notNull("user", builder.user);
+    Assert.notNull("inputSchema", builder.inputSchema);
+
+    this.database = builder.database;
+    this.databaseUrl = builder.databaseUrl;
+    this.user = builder.user;
+    this.inputSchema = builder.inputSchema;
+    this.password = builder.password == null ? "" : builder.password;
+  }
+
+  public String getConfiguration() {
+    return String.format(
+      """
+      <jdbc>
+        <url>%s</url>
+        <user>%s</user>
+        <password>%s</password>
+      </jdbc>
+      <generator>
+        <database>
+          <name>%s</name>
+          <includes>.*</includes>
+          <inputSchema>%s</inputSchema>
+        </database>
+        <target>
+          <packageName>org.jooq.codegen</packageName>
+          <directory>target/generated-sources/jooq</directory>
+        </target>
+      </generator>
+      """,
+      databaseUrl,
+      user,
+      password,
+      databaseJooqName(),
+      inputSchema
+    );
+  }
+
+  private String databaseJooqName() {
+    return switch (database) {
+      case POSTGRESQL -> "org.jooq.meta.postgres.PostgresDatabase";
+      case MYSQL -> "org.jooq.meta.mysql.MySQLDatabase";
+      case MARIADB -> "org.jooq.meta.mariadb.MariaDBDatabase";
+      case MSSQL -> "org.jooq.meta.sqlserver.SQLServerDatabase";
+    };
+  }
+
+  public static JooqModuleCodegenConfigurationBuilder builder() {
+    return new JooqModuleCodegenConfigurationBuilder();
+  }
+
+  public interface JooqModuleCodegenConfigurationDatabaseBuilder {
+    JooqModuleCodegenConfigurationDatabaseUrlBuilder database(DatabaseType database);
+  }
+
+  public interface JooqModuleCodegenConfigurationDatabaseUrlBuilder {
+    JooqModuleCodegenConfigurationUserBuilder databaseUrl(String databaseUrl);
+  }
+
+  public interface JooqModuleCodegenConfigurationUserBuilder {
+    JooqModuleCodegenConfigurationInputSchemaBuilder user(String user);
+  }
+
+  public interface JooqModuleCodegenConfigurationInputSchemaBuilder {
+    JooqModuleCodegenConfigurationBuilder inputSchema(String inputSchema);
+  }
+
+  public interface JooqModuleCodegenConfigurationPasswordBuilder {
+    JooqModuleCodegenConfiguration password(String password);
+  }
+
+  public static final class JooqModuleCodegenConfigurationBuilder
+    implements
+      JooqModuleCodegenConfigurationDatabaseBuilder,
+      JooqModuleCodegenConfigurationDatabaseUrlBuilder,
+      JooqModuleCodegenConfigurationUserBuilder,
+      JooqModuleCodegenConfigurationInputSchemaBuilder,
+      JooqModuleCodegenConfigurationPasswordBuilder {
+
+    private DatabaseType database;
+    private String databaseUrl;
+    private String user;
+    private String inputSchema;
+    private String password;
+
+    @Override
+    public JooqModuleCodegenConfigurationDatabaseUrlBuilder database(DatabaseType database) {
+      this.database = database;
+
+      return this;
+    }
+
+    @Override
+    public JooqModuleCodegenConfigurationUserBuilder databaseUrl(String databaseUrl) {
+      this.databaseUrl = databaseUrl;
+
+      return this;
+    }
+
+    @Override
+    public JooqModuleCodegenConfigurationInputSchemaBuilder user(String user) {
+      this.user = user;
+
+      return this;
+    }
+
+    @Override
+    public JooqModuleCodegenConfigurationBuilder inputSchema(String inputSchema) {
+      this.inputSchema = inputSchema;
+
+      return this;
+    }
+
+    @Override
+    public JooqModuleCodegenConfiguration password(String password) {
+      this.password = password;
+
+      return build();
+    }
+
+    public JooqModuleCodegenConfiguration build() {
+      return new JooqModuleCodegenConfiguration(this);
+    }
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteFeatureSlug.java b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteFeatureSlug.java
index cca8f6c2a..0eb9a7a86 100644
--- a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteFeatureSlug.java
+++ b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteFeatureSlug.java
@@ -21,6 +21,7 @@ public enum JHLiteFeatureSlug implements JHipsterFeatureSlugFactory {
   CODE_COVERAGE_JAVA("code-coverage-java"),
   JAVA_BUILD_TOOL("java-build-tool"),
   JAVA_BUILD_TOOL_WRAPPER("java-build-tool-wrapper"),
+  JOOQ("jooq"),
   JPA_PERSISTENCE("jpa-persistence"),
   LICENSE("license"),
   OAUTH_PROVIDER("oauth-provider"),
diff --git a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
index 66dd0ef69..d2d75d256 100644
--- a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
+++ b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
@@ -59,6 +59,10 @@ public enum JHLiteModuleSlug implements JHipsterModuleSlugFactory {
   JAVA_MEMOIZERS("java-memoizers"),
   JIB("jib"),
   JMOLECULES("jmolecules"),
+  JOOQ_MARIADB("jooq-mariadb"),
+  JOOQ_MSSQL("jooq-mssql"),
+  JOOQ_MYSQL("jooq-mysql"),
+  JOOQ_POSTGRESQL("jooq-postgresql"),
   JPA_PAGINATION("jpa-pagination"),
   JPA_MARIADB("jpa-mariadb"),
   JPA_MSSQL("jpa-mssql"),
diff --git a/src/main/resources/generator/dependencies/pom.xml b/src/main/resources/generator/dependencies/pom.xml
index 4b8438469..924e06478 100644
--- a/src/main/resources/generator/dependencies/pom.xml
+++ b/src/main/resources/generator/dependencies/pom.xml
@@ -71,10 +71,16 @@
     <openapi-maven-plugin.version>0.0.21</openapi-maven-plugin.version>
     <openapi-backwards-compat-maven-plugin.version>1.0.3</openapi-backwards-compat-maven-plugin.version>
     <langchain4j.version>0.36.0</langchain4j.version>
+    <jooq.version>3.19.13</jooq.version>
   </properties>
 
   <dependencyManagement>
     <dependencies>
+      <dependency>
+        <groupId>org.jooq</groupId>
+        <artifactId>jooq-codegen</artifactId>
+        <version>${jooq.version}</version>
+      </dependency>
       <dependency>
         <groupId>kr.motd.maven</groupId>
         <artifactId>os-maven-plugin</artifactId>


Test patch:
diff --git a/src/main/resources/generator/server/springboot/database/jpa/MsSQLTestContainerExtension.java.mustache b/src/main/resources/generator/server/springboot/database/common/MsSQLTestContainerExtension.java.mustache
similarity index 100%
rename from src/main/resources/generator/server/springboot/database/jpa/MsSQLTestContainerExtension.java.mustache
rename to src/main/resources/generator/server/springboot/database/common/MsSQLTestContainerExtension.java.mustache
diff --git a/src/main/resources/generator/server/springboot/database/jpa/databaseType.md.mustache b/src/main/resources/generator/server/springboot/database/common/databaseType.md.mustache
similarity index 100%
rename from src/main/resources/generator/server/springboot/database/jpa/databaseType.md.mustache
rename to src/main/resources/generator/server/springboot/database/common/databaseType.md.mustache
diff --git a/src/main/resources/generator/server/springboot/database/jpa/docker/container-license-acceptance.txt.mustache b/src/main/resources/generator/server/springboot/database/common/docker/container-license-acceptance.txt.mustache
similarity index 100%
rename from src/main/resources/generator/server/springboot/database/jpa/docker/container-license-acceptance.txt.mustache
rename to src/main/resources/generator/server/springboot/database/common/docker/container-license-acceptance.txt.mustache
diff --git a/src/main/resources/generator/server/springboot/database/jpa/docker/mariadb.yml.mustache b/src/main/resources/generator/server/springboot/database/common/docker/mariadb.yml.mustache
similarity index 100%
rename from src/main/resources/generator/server/springboot/database/jpa/docker/mariadb.yml.mustache
rename to src/main/resources/generator/server/springboot/database/common/docker/mariadb.yml.mustache
diff --git a/src/main/resources/generator/server/springboot/database/jpa/docker/mssql.yml.mustache b/src/main/resources/generator/server/springboot/database/common/docker/mssql.yml.mustache
similarity index 100%
rename from src/main/resources/generator/server/springboot/database/jpa/docker/mssql.yml.mustache
rename to src/main/resources/generator/server/springboot/database/common/docker/mssql.yml.mustache
diff --git a/src/main/resources/generator/server/springboot/database/jpa/docker/mysql.yml.mustache b/src/main/resources/generator/server/springboot/database/common/docker/mysql.yml.mustache
similarity index 100%
rename from src/main/resources/generator/server/springboot/database/jpa/docker/mysql.yml.mustache
rename to src/main/resources/generator/server/springboot/database/common/docker/mysql.yml.mustache
diff --git a/src/main/resources/generator/server/springboot/database/jpa/docker/postgresql.yml.mustache b/src/main/resources/generator/server/springboot/database/common/docker/postgresql.yml.mustache
similarity index 100%
rename from src/main/resources/generator/server/springboot/database/jpa/docker/postgresql.yml.mustache
rename to src/main/resources/generator/server/springboot/database/common/docker/postgresql.yml.mustache
diff --git a/src/test/features/server/springboot/database/jooq-mariadb.feature b/src/test/features/server/springboot/database/jooq-mariadb.feature
new file mode 100644
index 000000000..7fcf3cc0f
--- /dev/null
+++ b/src/test/features/server/springboot/database/jooq-mariadb.feature
@@ -0,0 +1,41 @@
+Feature: Jooq MariaDB module
+
+  Scenario: Should add MariaDB elements using legacy url
+    When I apply modules to default project
+      | maven-java   |
+      | spring-boot  |
+      | jooq-mariadb |
+    Then I should have files in ""
+      | pom.xml |
+    And I should have files in "documentation"
+      | mariadb.md |
+    And I should have files in "src/main/docker"
+      | mariadb.yml |
+    And I should have files in "src/main/resources/config"
+      | application.yml |
+    And I should have files in "src/test/resources/config"
+      | application-test.yml |
+
+  Scenario: Should get MariaDB module properties definition
+    When I get module "jooq-mariadb" properties definition
+    Then I should have properties definitions
+      | Key                       | Type    | Mandatory |
+      | packageName               | STRING  | true      |
+      | baseName                  | STRING  | true      |
+      | indentSize                | INTEGER | false     |
+      | springConfigurationFormat | STRING  | false     |
+
+  Scenario: Should add MariaDB elements using module url
+    When I apply "jooq-mariadb" module to default project with maven file
+      | packageName | tech.jhipster.chips |
+      | baseName    | jhipster            |
+    Then I should have files in ""
+      | pom.xml |
+    And I should have files in "documentation"
+      | mariadb.md |
+    And I should have files in "src/main/docker"
+      | mariadb.yml |
+    And I should have files in "src/main/resources/config"
+      | application.yml |
+    And I should have files in "src/test/resources/config"
+      | application-test.yml |
diff --git a/src/test/features/server/springboot/database/jooq-mssql.feature b/src/test/features/server/springboot/database/jooq-mssql.feature
new file mode 100644
index 000000000..5c430b284
--- /dev/null
+++ b/src/test/features/server/springboot/database/jooq-mssql.feature
@@ -0,0 +1,30 @@
+Feature: Jooq MsSql module
+
+  Scenario: Should add MsSQL elements using legacy url
+    When I apply modules to default project
+      | maven-java  |
+      | spring-boot |
+      | jooq-mssql  |
+    Then I should have files in ""
+      | pom.xml |
+    And I should have files in "documentation"
+      | mssql.md |
+    And I should have files in "src/main/docker"
+      | mssql.yml |
+    And I should have files in "src/test/java/tech/jhipster/chips"
+      | MsSQLTestContainerExtension.java |
+    And I should have files in "src/test/resources"
+      | container-license-acceptance.txt |
+    And I should have files in "src/main/resources/config"
+      | application.yml |
+    And I should have files in "src/test/resources/config"
+      | application-test.yml |
+
+  Scenario: Should get MsSQL module properties definition
+    When I get module "jooq-mssql" properties definition
+    Then I should have properties definitions
+      | Key                       | Type    | Mandatory |
+      | packageName               | STRING  | true      |
+      | baseName                  | STRING  | true      |
+      | indentSize                | INTEGER | false     |
+      | springConfigurationFormat | STRING  | false     |
diff --git a/src/test/features/server/springboot/database/jooq-mysql.feature b/src/test/features/server/springboot/database/jooq-mysql.feature
new file mode 100644
index 000000000..3d597ef3e
--- /dev/null
+++ b/src/test/features/server/springboot/database/jooq-mysql.feature
@@ -0,0 +1,41 @@
+Feature: Jooq MySQL module
+
+  Scenario: Should add MySQL elements using legacy url
+    When I apply modules to default project
+      | maven-java  |
+      | spring-boot |
+      | jooq-mysql  |
+    Then I should have files in ""
+      | pom.xml |
+    And I should have files in "documentation"
+      | mysql.md |
+    And I should have files in "src/main/docker"
+      | mysql.yml |
+    And I should have files in "src/main/resources/config"
+      | application.yml |
+    And I should have files in "src/test/resources/config"
+      | application-test.yml |
+
+  Scenario: Should get MySQL module properties definition
+    When I get module "jooq-mysql" properties definition
+    Then I should have properties definitions
+      | Key                       | Type    | Mandatory |
+      | packageName               | STRING  | true      |
+      | baseName                  | STRING  | true      |
+      | indentSize                | INTEGER | false     |
+      | springConfigurationFormat | STRING  | false     |
+
+  Scenario: Should add MySQL elements using module url
+    When I apply "jooq-mysql" module to default project with maven file
+      | packageName | tech.jhipster.chips |
+      | baseName    | jhipster            |
+    Then I should have files in ""
+      | pom.xml |
+    And I should have files in "documentation"
+      | mysql.md |
+    And I should have files in "src/main/docker"
+      | mysql.yml |
+    And I should have files in "src/main/resources/config"
+      | application.yml |
+    And I should have files in "src/test/resources/config"
+      | application-test.yml |
diff --git a/src/test/features/server/springboot/database/jooq-postgresql.feature b/src/test/features/server/springboot/database/jooq-postgresql.feature
new file mode 100644
index 000000000..d4b6c46fd
--- /dev/null
+++ b/src/test/features/server/springboot/database/jooq-postgresql.feature
@@ -0,0 +1,41 @@
+Feature: Jooq PostgreSQL module
+
+  Scenario: Should add postgreSQL elements using legacy url
+    When I apply modules to default project
+      | maven-java      |
+      | spring-boot     |
+      | jooq-postgresql |
+    Then I should have files in ""
+      | pom.xml |
+    And I should have files in "documentation"
+      | postgresql.md |
+    And I should have files in "src/main/docker"
+      | postgresql.yml |
+    And I should have files in "src/main/resources/config"
+      | application.yml |
+    And I should have files in "src/test/resources/config"
+      | application-test.yml |
+
+  Scenario: Should get postgreSQL module properties definition
+    When I get module "jooq-postgresql" properties definition
+    Then I should have properties definitions
+      | Key                       | Type    | Mandatory |
+      | packageName               | STRING  | true      |
+      | baseName                  | STRING  | true      |
+      | indentSize                | INTEGER | false     |
+      | springConfigurationFormat | STRING  | false     |
+
+  Scenario: Should add postgreSQL elements using module url
+    When I apply "jooq-postgresql" module to default project with maven file
+      | packageName | tech.jhipster.chips |
+      | baseName    | jhipster            |
+    Then I should have files in ""
+      | pom.xml |
+    And I should have files in "documentation"
+      | postgresql.md |
+    And I should have files in "src/main/docker"
+      | postgresql.yml |
+    And I should have files in "src/main/resources/config"
+      | application.yml |
+    And I should have files in "src/test/resources/config"
+      | application-test.yml |
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/JooqModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/JooqModuleFactoryTest.java
new file mode 100644
index 000000000..0773df34c
--- /dev/null
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/jooq/domain/JooqModuleFactoryTest.java
@@ -0,0 +1,323 @@
+package tech.jhipster.lite.generator.server.springboot.database.jooq.domain;
+
+import static org.mockito.Mockito.when;
+import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import tech.jhipster.lite.TestFileUtils;
+import tech.jhipster.lite.UnitTest;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.JHipsterModulesFixture;
+import tech.jhipster.lite.module.domain.docker.DockerImageVersion;
+import tech.jhipster.lite.module.domain.docker.DockerImages;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+import tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions;
+
+@UnitTest
+@ExtendWith(MockitoExtension.class)
+class JooqModuleFactoryTest {
+
+  @Mock
+  private DockerImages dockerImages;
+
+  @InjectMocks
+  private JooqModuleFactory factory;
+
+  @Test
+  void shouldCreatePostgresqlModule() {
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest())
+      .basePackage("tech.jhipster.jhlitest")
+      .projectBaseName("myapp")
+      .build();
+
+    when(dockerImages.get("postgres")).thenReturn(new DockerImageVersion("postgres", "0.0.0"));
+
+    JHipsterModule module = factory.buildPostgresql(properties);
+
+    assertThatModuleWithFiles(module, pomFile())
+      .hasFile("documentation/postgresql.md")
+      .containing("docker compose -f src/main/docker/postgresql.yml up -d")
+      .and()
+      .hasFile("pom.xml")
+      .containing(
+        """
+            <dependency>
+              <groupId>org.postgresql</groupId>
+              <artifactId>postgresql</artifactId>
+              <scope>runtime</scope>
+            </dependency>
+        """
+      )
+      .containing("<groupId>com.zaxxer</groupId>")
+      .containing("<artifactId>HikariCP</artifactId>")
+      .containing(
+        """
+            <dependency>
+              <groupId>org.springframework.boot</groupId>
+              <artifactId>spring-boot-starter-jooq</artifactId>
+            </dependency>
+        """
+      )
+      .containing("<groupId>org.testcontainers</groupId>")
+      .containing("<artifactId>postgresql</artifactId>")
+      .containing("<artifactId>jooq-codegen-maven</artifactId>")
+      .and()
+      .hasFile("src/main/resources/config/application.yml")
+      .containing(
+        """
+        spring:
+          datasource:
+            driver-class-name: org.postgresql.Driver
+            hikari:
+              auto-commit: false
+              poolName: Hikari
+            password: ''
+            type: com.zaxxer.hikari.HikariDataSource
+            url: jdbc:postgresql://localhost:5432/myapp
+            username: myapp
+        """
+      )
+      .and()
+      .hasFile("src/test/resources/config/application-test.yml")
+      .containing(
+        """
+        spring:
+          datasource:
+            driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
+            hikari:
+              maximum-pool-size: 2
+            password: ''
+            url: jdbc:tc:postgresql:0.0.0:///myapp?TC_TMPFS=/testtmpfs:rw
+            username: myapp
+        """
+      );
+  }
+
+  @Test
+  void shouldCreateMariadbModule() {
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest())
+      .basePackage("tech.jhipster.jhlitest")
+      .projectBaseName("myapp")
+      .build();
+
+    when(dockerImages.get("mariadb")).thenReturn(new DockerImageVersion("mariadb", "0.0.0"));
+
+    JHipsterModule module = factory.buildMariaDB(properties);
+
+    assertThatModuleWithFiles(module, pomFile())
+      .hasFile("documentation/mariadb.md")
+      .containing("docker compose -f src/main/docker/mariadb.yml up -d")
+      .and()
+      .hasPrefixedFiles("src/main/docker", "mariadb.yml")
+      .hasFile("pom.xml")
+      .containing(
+        """
+            <dependency>
+              <groupId>org.mariadb.jdbc</groupId>
+              <artifactId>mariadb-java-client</artifactId>
+              <scope>runtime</scope>
+            </dependency>
+        """
+      )
+      .containing("<groupId>com.zaxxer</groupId>")
+      .containing("<artifactId>HikariCP</artifactId>")
+      .containing(
+        """
+            <dependency>
+              <groupId>org.springframework.boot</groupId>
+              <artifactId>spring-boot-starter-jooq</artifactId>
+            </dependency>
+        """
+      )
+      .containing("<groupId>org.testcontainers</groupId>")
+      .containing("<artifactId>mariadb</artifactId>")
+      .containing("<artifactId>jooq-codegen-maven</artifactId>")
+      .and()
+      .hasFile("src/main/resources/config/application.yml")
+      .containing(
+        """
+        spring:
+          datasource:
+            driver-class-name: org.mariadb.jdbc.Driver
+            hikari:
+              auto-commit: false
+              poolName: Hikari
+            password: ''
+            type: com.zaxxer.hikari.HikariDataSource
+            url: jdbc:mariadb://localhost:3306/myapp
+            username: root
+        """
+      )
+      .and()
+      .hasFile("src/test/resources/config/application-test.yml")
+      .containing(
+        """
+        spring:
+          datasource:
+            driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
+            hikari:
+              maximum-pool-size: 2
+            password: ''
+            url: jdbc:tc:mariadb:0.0.0:///myapp
+            username: myapp
+        """
+      );
+  }
+
+  @Test
+  void shouldCreateMysqlModule() {
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest())
+      .basePackage("tech.jhipster.jhlitest")
+      .projectBaseName("myapp")
+      .build();
+
+    when(dockerImages.get("mysql")).thenReturn(new DockerImageVersion("mysql", "0.0.0"));
+
+    JHipsterModule module = factory.buildMySQL(properties);
+
+    assertThatModuleWithFiles(module, pomFile())
+      .hasFile("documentation/mysql.md")
+      .containing("docker compose -f src/main/docker/mysql.yml up -d")
+      .and()
+      .hasPrefixedFiles("src/main/docker", "mysql.yml")
+      .hasFile("pom.xml")
+      .containing(
+        """
+            <dependency>
+              <groupId>com.mysql</groupId>
+              <artifactId>mysql-connector-j</artifactId>
+              <scope>runtime</scope>
+            </dependency>
+        """
+      )
+      .containing("<groupId>com.zaxxer</groupId>")
+      .containing("<artifactId>HikariCP</artifactId>")
+      .containing(
+        """
+            <dependency>
+              <groupId>org.springframework.boot</groupId>
+              <artifactId>spring-boot-starter-jooq</artifactId>
+            </dependency>
+        """
+      )
+      .containing("<groupId>org.testcontainers</groupId>")
+      .containing("<artifactId>mysql</artifactId>")
+      .containing("<artifactId>jooq-codegen-maven</artifactId>")
+      .and()
+      .hasFile("src/main/resources/config/application.yml")
+      .containing(
+        // language=yaml
+        """
+        spring:
+          datasource:
+            driver-class-name: com.mysql.cj.jdbc.Driver
+            hikari:
+              auto-commit: false
+              poolName: Hikari
+            password: ''
+            type: com.zaxxer.hikari.HikariDataSource
+            url: jdbc:mysql://localhost:3306/myapp
+            username: root
+        """
+      )
+      .and()
+      .hasFile("src/test/resources/config/application-test.yml")
+      .containing(
+        // language=yaml
+        """
+        spring:
+          datasource:
+            driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
+            hikari:
+              maximum-pool-size: 2
+            password: ''
+            url: jdbc:tc:mysql:0.0.0:///myapp
+            username: myapp
+        """
+      );
+  }
+
+  @Test
+  void shouldCreateMssqlModule() {
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest())
+      .basePackage("tech.jhipster.jhlitest")
+      .projectBaseName("myapp")
+      .build();
+
+    when(dockerImages.get("mcr.microsoft.com/mssql/server")).thenReturn(new DockerImageVersion("mcr.microsoft.com/mssql/server", "0.0.0"));
+
+    JHipsterModule module = factory.buildMsSQL(properties);
+
+    assertThatModuleWithFiles(module, pomFile(), integrationTestAnnotation())
+      .hasFile("documentation/mssql.md")
+      .containing("docker compose -f src/main/docker/mssql.yml up -d")
+      .and()
+      .hasFile("src/test/java/tech/jhipster/jhlitest/MsSQLTestContainerExtension.java")
+      .and()
+      .hasFile("src/test/resources/container-license-acceptance.txt")
+      .and()
+      .hasFile("pom.xml")
+      .containing(
+        """
+            <dependency>
+              <groupId>com.microsoft.sqlserver</groupId>
+              <artifactId>mssql-jdbc</artifactId>
+              <scope>runtime</scope>
+            </dependency>
+        """
+      )
+      .containing("<groupId>com.zaxxer</groupId>")
+      .containing("<artifactId>HikariCP</artifactId>")
+      .containing(
+        """
+            <dependency>
+              <groupId>org.springframework.boot</groupId>
+              <artifactId>spring-boot-starter-jooq</artifactId>
+            </dependency>
+        """
+      )
+      .containing("<groupId>org.testcontainers</groupId>")
+      .containing("<artifactId>mssqlserver</artifactId>")
+      .containing("<artifactId>jooq-codegen-maven</artifactId>")
+      .and()
+      .hasFile("src/main/resources/config/application.yml")
+      .containing(
+        // language=yaml
+        """
+        spring:
+          datasource:
+            driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
+            hikari:
+              auto-commit: false
+              poolName: Hikari
+            password: yourStrong(!)Password
+            type: com.zaxxer.hikari.HikariDataSource
+            url: jdbc:sqlserver://localhost:1433;database=myapp;trustServerCertificate=true
+            username: SA
+        """
+      )
+      .and()
+      .hasFile("src/test/resources/config/application-test.yml")
+      .containing(
+        // language=yaml
+        """
+        spring:
+          datasource:
+            driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
+            hikari:
+              maximum-pool-size: 2
+            password: yourStrong(!)Password
+            url: jdbc:tc:sqlserver:///;database=myapp;trustServerCertificate=true?TC_TMPFS=/testtmpfs:rw
+            username: SA
+        """
+      );
+  }
+
+  private JHipsterModulesAssertions.ModuleFile integrationTestAnnotation() {
+    return file("src/test/resources/projects/files/IntegrationTest.java", "src/test/java/tech/jhipster/jhlitest/IntegrationTest.java");
+  }
+}