Repository: Blackdread/sql-to-jdl Branch: master Commit: fe33c6dd1bd3 Files: 203 Total size: 316.5 KB Directory structure: gitextract_dpof0rjx/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── maven.yml ├── .gitignore ├── .mvn/ │ └── wrapper/ │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── checkstyle-suppressions.xml ├── checkstyle.xml ├── mvnw ├── mvnw.cmd ├── pom.xml └── src/ ├── main/ │ ├── java/ │ │ └── org/ │ │ └── blackdread/ │ │ └── sqltojava/ │ │ ├── SqlToJavaApplication.java │ │ ├── config/ │ │ │ ├── ApplicationProperties.java │ │ │ ├── CacheConfiguration.java │ │ │ ├── DatabaseObjectTypesConfigEnum.java │ │ │ ├── ExportFileStructureType.java │ │ │ └── UndefinedJdlTypeHandlingEnum.java │ │ ├── entity/ │ │ │ ├── JdlEntity.java │ │ │ ├── JdlField.java │ │ │ ├── JdlFieldEnum.java │ │ │ ├── JdlRelation.java │ │ │ ├── JdlRelationGroup.java │ │ │ ├── RelationType.java │ │ │ ├── SqlColumn.java │ │ │ ├── SqlForeignKey.java │ │ │ ├── SqlTable.java │ │ │ └── impl/ │ │ │ ├── JdlEntityImpl.java │ │ │ ├── JdlFieldImpl.java │ │ │ ├── JdlRelationGroupImpl.java │ │ │ ├── JdlRelationImpl.java │ │ │ ├── SqlColumnImpl.java │ │ │ └── SqlTableImpl.java │ │ ├── exporter/ │ │ │ └── ExportFileStructureConfig.java │ │ ├── listener/ │ │ │ └── SetDatabaseProfileApplicationEventListener.java │ │ ├── parser/ │ │ │ └── SqlParser.java │ │ ├── pojo/ │ │ │ ├── ColumnInformation.java │ │ │ ├── TableInformation.java │ │ │ ├── TableRelationInformation.java │ │ │ └── rowmaper/ │ │ │ ├── ColumnInformationRowMapper.java │ │ │ ├── SqlServerColumnInformationRowMapper.java │ │ │ ├── SqlServerTableInformationRowMapper.java │ │ │ ├── TableInformationRowMapper.java │ │ │ └── TableRelationInformationRowMapper.java │ │ ├── repository/ │ │ │ ├── InformationSchemaRepository.java │ │ │ ├── MsSqlPureSqlInformationSchemaRepository.java │ │ │ └── PureSqlInformationSchemaRepository.java │ │ ├── service/ │ │ │ ├── InformationSchemaService.java │ │ │ ├── MsSqlJdlTypeService.java │ │ │ ├── MySqlJdlTypeService.java │ │ │ ├── OracleJdlTypeService.java │ │ │ ├── PostgresJdlTypeService.java │ │ │ ├── SqlJdlTypeService.java │ │ │ └── logic/ │ │ │ ├── ExportService.java │ │ │ ├── JdlService.java │ │ │ ├── MustacheService.java │ │ │ └── SqlService.java │ │ ├── util/ │ │ │ ├── AppUtil.java │ │ │ ├── JdbcUtil.java │ │ │ ├── JdlUtils.java │ │ │ ├── NamingConventionUtil.java │ │ │ ├── ResourceUtil.java │ │ │ └── SqlUtils.java │ │ └── view/ │ │ ├── JdlCommentView.java │ │ ├── JdlEntityView.java │ │ ├── JdlFieldView.java │ │ ├── JdlRelationGroupView.java │ │ ├── JdlRelationView.java │ │ ├── impl/ │ │ │ ├── JdlEntityViewImpl.java │ │ │ ├── JdlFieldViewImpl.java │ │ │ ├── JdlRelationGroupViewImpl.java │ │ │ └── JdlRelationViewImpl.java │ │ └── mapper/ │ │ ├── JdlViewMapper.java │ │ └── OptionalUtils.java │ └── resources/ │ ├── application.yml │ ├── mustache/ │ │ ├── application-entities-relations-views.mustache │ │ ├── application-grouped-relations-separate-views.mustache │ │ ├── application.mustache │ │ ├── entities.mustache │ │ ├── groupedmanytoonerelations.mustache │ │ ├── groupedonetoonerelations.mustache │ │ ├── manytomanyrelations.mustache │ │ ├── manytoonerelations.mustache │ │ ├── onetoonerelations.mustache │ │ ├── options.mustache │ │ ├── relations.mustache │ │ ├── views.mustache │ │ └── viewsrelations.mustache │ ├── reserved/ │ │ └── keywords.json │ └── sql/ │ ├── mysql_mariadb-getAllTableInformation.sql │ ├── mysql_mariadb-getAllTableRelationInformation.sql │ ├── mysql_mariadb-getFullColumnInformationOfTable.sql │ ├── oracle-getAllTableInformation.sql │ ├── oracle-getAllTableRelationInformation.sql │ ├── oracle-getFullColumnInformationOfTable.sql │ ├── postgresql-getAllTableInformation.sql │ ├── postgresql-getAllTableRelationInformation.sql │ ├── postgresql-getFullColumnInformationOfTable.sql │ ├── sqlserver-getAllTableInformation.sql │ ├── sqlserver-getAllTableRelationInformation.sql │ └── sqlserver-getFullColumnInformationOfTable.sql └── test/ ├── java/ │ └── org/ │ └── blackdread/ │ └── sqltojava/ │ ├── AssertUtil.java │ ├── FileUtil.java │ ├── shared/ │ │ ├── LoggingExtension.java │ │ ├── MainApplicationContextLoader.java │ │ ├── interfaces/ │ │ │ ├── CompareJdlResultsTest.java │ │ │ ├── ContainersStartedTest.java │ │ │ ├── EnvironmentTest.java │ │ │ ├── JdbcContainerTest.java │ │ │ ├── LoggingTest.java │ │ │ └── ProfileActiveTest.java │ │ └── tests/ │ │ ├── BaseJdbcContainerTest.java │ │ ├── SqlToJdlTransactionPerTestTest.java │ │ └── TransactionPerTestTest.java │ └── test/ │ ├── db/ │ │ ├── mssql/ │ │ │ └── MssqlSql2019Test.java │ │ ├── mysql/ │ │ │ ├── MariaDBLatestTest.java │ │ │ ├── Mysql57Test.java │ │ │ ├── Mysql8Test.java │ │ │ ├── Mysql9Test.java │ │ │ └── MysqlLatestTest.java │ │ ├── oracle/ │ │ │ └── OracleLatestTest.java │ │ └── postgres/ │ │ ├── Postgres09Test.java │ │ ├── Postgres10Test.java │ │ ├── Postgres15Test.java │ │ ├── Postgres17Test.java │ │ ├── PostgresAddTableNameJdlTest.java │ │ ├── PostgresDatabaseObjectPrefixTest.java │ │ ├── PostgresLatestTest.java │ │ └── PostgresOverrideJdlTypeTest.java │ └── general/ │ ├── DatabaseObjectPrefixTest.java │ ├── MustacheTest.java │ ├── unsupported/ │ │ ├── PostgresUndefinedErrorTest.java │ │ ├── PostgresUndefinedSkioTest.java │ │ └── PostgresUndefinedUnsupportedTest.java │ └── views/ │ └── PostgresDatabaseObjectTypesTest.java └── resources/ ├── application.yml ├── container-license-acceptance.txt ├── db/ │ └── changelog/ │ └── db.changelog-master.yaml ├── jdl/ │ ├── all_jdl_types-expected.jdl │ ├── all_types-expected-mariadb.jdl │ ├── all_types-expected-mysql.jdl │ ├── all_types-expected-oracle.jdl │ ├── all_types-expected-postgresql.jdl │ ├── all_types-expected-sqlserver.jdl │ ├── all_types-liquibase-changeset-sqlserver.yaml │ ├── all_types-liquibase-changeset.yaml │ ├── display_field_many_to_one-expected-sqlserver.jdl │ ├── display_field_many_to_one-expected.jdl │ ├── display_field_many_to_one-liquibase-changeset.yaml │ ├── duplicate_names-expected.jdl │ ├── duplicate_names-liquibase-changeset.yaml │ ├── enum-expected-sqlserver.jdl │ ├── enum-expected.jdl │ ├── enum-liquibase-changeset-sqlserver.yaml │ ├── enum-liquibase-changeset.yaml │ ├── many_to_one-expected.jdl │ ├── many_to_one-liquibase-changeset.yaml │ ├── one_to_one-expected.jdl │ ├── one_to_one-liquibase-changeset.yaml │ ├── one_to_one_main_map-expected.jdl │ ├── one_to_one_main_map-liquibase-changeset.yaml │ ├── override_jdl_types-expected-postgresql.jdl │ ├── override_jdl_types-liquibase-changeset.yaml │ ├── parent_child-expected.jdl │ ├── parent_child-liquibase-changeset.yaml │ ├── prefix-expected.jdl │ ├── prefix-liquibase-changeset.yaml │ ├── prune-expected.jdl │ ├── prune-liquibase-changeset.yaml │ ├── readonly-expected-sqlserver.jdl │ ├── readonly-expected.jdl │ ├── readonly-liquibase-changeset.yaml │ ├── reflexive_relationship-expected.jdl │ ├── reflexive_relationship-liquibase-changeset.yaml │ ├── table_name-expected.jdl │ ├── table_name-liquibase-changeset.yaml │ ├── tables-only-expected.jdl │ ├── tables-only-liquibase-changeset.yaml │ ├── undefined_error-expected.jdl │ ├── undefined_error-liquibase-changeset.yaml │ ├── undefined_skip-expected.jdl │ ├── undefined_skip-liquibase-changeset.yaml │ ├── undefined_unsupported-expected.jdl │ ├── undefined_unsupported-liquibase-changeset.yaml │ ├── unique-expected-oracle.jdl │ ├── unique-expected-sqlserver.jdl │ ├── unique-expected.jdl │ ├── unique-liquibase-changeset.yaml │ ├── uuid_id_required-expected-mariadb.jdl │ ├── uuid_id_required-expected-mysql.jdl │ ├── uuid_id_required-expected-postgresql.jdl │ ├── uuid_id_required-expected-sqlserver.jdl │ ├── uuid_id_required-expected.jdl │ ├── uuid_id_required-liquibase-changeset-sqlserver.yaml │ ├── uuid_id_required-liquibase-changeset.yaml │ ├── views-expected.jdl │ └── views-liquibase-changeset.yaml └── liquibase.properties ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] # Change these settings to your own preference indent_style = space indent_size = 4 # We recommend you to keep these unchanged end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false [{package,bower}.json] indent_style = space indent_size = 2 ================================================ FILE: .gitattributes ================================================ # All text files should have the "lf" (Unix) line endings * text eol=lf # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. *.java text *.js text *.css text *.html text # Denote all files that are truly binary and should not be modified. *.png binary *.jpg binary *.jpeg binary *.gif binary *.ico binary *.jar binary *.pdf binary *.eot binary *.ttf binary *.gzip binary *.gz binary *.ai binary *.eps binary *.swf binary *.mwb binary ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: maven directory: "/" schedule: interval: monthly open-pull-requests-limit: 15 groups: maven-dependencies: update-types: - "patch" - "minor" - "major" ================================================ FILE: .github/workflows/maven.yml ================================================ name: Java CI on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: 'adopt' java-version: '17' cache: 'maven' - name: Build and Run Tests run: mvn test --file pom.xml --batch-mode --update-snapshots --fail-at-end - name: Publish Test Report if: ${{ always() }} uses: scacap/action-surefire-report@v1 ================================================ FILE: .gitignore ================================================ ###################### # Project Specific ###################### classes/artifacts /my-project-jdl.jh /*.jdl /*.jh ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### log/ target/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### #/build/ #/bin/ #/deploy/ ###################### # Logs ###################### *.log ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: .mvn/wrapper/maven-wrapper.properties ================================================ distributionUrl=https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.zip ================================================ FILE: .prettierignore ================================================ ================================================ FILE: .prettierrc ================================================ # Prettier configuration printWidth: 140 singleQuote: true tabWidth: 2 useTabs: false # java rules: overrides: - files: "*.java" options: tabWidth: 4 ================================================ FILE: .travis.yml ================================================ language: java #jdk: # - oraclejdk8 cache: directories: - $HOME/.m2 services: - mysql before_install: - chmod +x mvnw after_success: - mvn clean test # - mvn clean test jacoco:report # - mvn clean test jacoco:report coveralls:report #notifications: # email: # - yoann.caplain@blackdread.org ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Yoann CAPLAIN Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![build](https://github.com/Blackdread/sql-to-jdl/actions/workflows/maven.yml/badge.svg)](https://github.com/Blackdread/sql-to-jdl/actions/workflows/maven.yml) [![StackShare](https://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](https://stackshare.io/Blackdread/sql-to-jdl) # sql-to-jdl Tool to translate SQL databases to JDL format of jHipster (Created due to existing databases to be generated with jHipster and build angular-java web) # Usage ```bash #Download git clone https://github.com/Blackdread/sql-to-jdl.git # Modify configuration file application.yml at https://github.com/Blackdread/sql-to-jdl/blob/master/src/main/resources/application.yml #Build, skip the tests for lack of time mvn clean package -DskipTests #Run java -jar target/sql-to-java-*-SNAPSHOT.jar ``` Direct link to [application.yml](https://github.com/Blackdread/sql-to-jdl/blob/master/src/main/resources/application.yml) _Don't forget to install git, maven 3 and java 17 before launching_ # Compatibility This implementation works with - mysql 5.7.x, 8.x, 9.x - mariadb 10.x - postgresql 9.x+ - oracle 21 - MsSQL 2019 Help is requested on MsSQL and Oracle support. This will require implementation of SqlJdlTypeService and creating the raw SQL files. See the MySQL, MariaDB, and PosrgreSQL implemtaitons for examples. # Testing Setting up tests for new databases types or versions is easy. Have a look existing tests. MsSQL and Oracle have tests created that are disabled because their implementations are incomplete. Tests use liquibase so that most of the setup for tests is database agnostic. The main exception is for the all types test that is different for each database type. Each test needs the following files - {{test.name}}-liquibase-changeset.yaml - {{test.name}}-expected.jdl To override the default name the files as - {{test.name}}-liquibase-changeset-{{db.typ}}.yaml - {{test.name}}-expected-{{db.type}}.jdl If you need to change the tests run for a specific database type or version, use method hiding by implementing private static Stream provideTestNames() in the subclass of SqlToJdlTransactionPerTestTest In order to avoid starting a new database and spring boot container for every test, liquibase is used to roll back the database to an empty state. This can be done by just rolling back the transaction with Spring, but MySQL does not support rollback of DDL changes. This works great with PostgreSQL, but liquibase is used to roll back the changes for all database types currently. Currently the full test suite runs 11 test per database version which total nearly 80 tests. # Why not use tools like UML provided on jHipster? - JDL from web is ok for a few entities but not for more than 100 entities and relations - UML software and xml exporters could have worked (other tools on jHipster) but: - already many databases in production to be exported in JDL (faster to generate the JDL from it) - already working UML design with MySQL Workbench # How to use Just execute the code from IDE or use "mvn" to run the code, it will connect to your DB (see application config yml) and it will generate the JDL. Set properties file: - Schema name to export - Tables names to be ignored - Path of export file - Database object prefixes to remove from entity name - Include table name is JDL - Undefined JDL type handling to ERROR, SKIP, or UNSUPPORTED - Add JDL type overrides if necessary. # After JDL file is generated Still have some manual steps to do: - review relations: - ManyToMany - Owner side display field - Inverse side field name and display field - Bidirectional or not - add values to enums - review validations of entities # Default specific rules Table is treated as enum if only 2 columns and both are: "id" AND ("code" OR "name") Table is treated as ManyToMany if only 2 columns and both are foreign keys # Links [jHipster JDL](https://www.jhipster.tech/jdl/intro) # An alternative for REST filter and sort Different criterias, support for JPA and jOOQ dynamic filtering and sorting. https://github.com/Blackdread/rest-filter ================================================ FILE: checkstyle-suppressions.xml ================================================ ================================================ FILE: checkstyle.xml ================================================ ================================================ FILE: mvnw ================================================ #!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Maven2 Start Up Batch script # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then export JAVA_HOME="`/usr/libexec/java_home`" else export JAVA_HOME="/Library/Java/Home" fi fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=`java-config --jre-home` fi fi if [ -z "$M2_HOME" ] ; then ## resolve links - $0 may be a link to maven's home PRG="$0" # need this for relative symlinks while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG="`dirname "$PRG"`/$link" fi done saveddir=`pwd` M2_HOME=`dirname "$PRG"`/.. # make it fully qualified M2_HOME=`cd "$M2_HOME" && pwd` cd "$saveddir" # echo Using m2 at $M2_HOME fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi # For Migwn, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then javaExecutable="`which javac`" if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=`which readlink` if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then if $darwin ; then javaHome="`dirname \"$javaExecutable\"`" javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" else javaExecutable="`readlink -f \"$javaExecutable\"`" fi javaHome="`dirname \"$javaExecutable\"`" javaHome=`expr "$javaHome" : '\(.*\)/bin'` JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="`which java`" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=`cd "$wdir/.."; pwd` fi # end of workaround done echo "${basedir}" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then echo "$(tr -s '\n' ' ' < "$1")" fi } BASE_DIR=`find_maven_basedir "$(pwd)"` if [ -z "$BASE_DIR" ]; then exit 1; fi export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} echo $MAVEN_PROJECTBASEDIR MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" ================================================ FILE: mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Maven2 Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%" == "on" pause if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% exit /B %ERROR_CODE% ================================================ FILE: pom.xml ================================================ 4.0.0 org.springframework.boot spring-boot-starter-parent 2.7.4 org.blackdread sql-to-java 1.0-SNAPSHOT jar sql-to-java Sql to JDL project UTF-8 UTF-8 17 jdbc:mysql://localhost:3306/?serverTimezone=UTC root 33.3.1-jre 1.17.6 write 3.5.1 3.5.0 4.19.0 4.19.0 10.18.2 check 0.9.10 1.6.2 scm:git:https://github.com/Blackdread/sql-to-jdl.git scm:git:https://github.com/blackdread/sql-to-jdl.git https://github.com/blackdread/sql-to-jdl MIT https://github.com/Blackdread/sql-to-jdl/blob/master/LICENSE repo blackdread Yoann CAPLAIN yoann.caplain@blackdread.org http://blackdread.org com.google.guava guava ${guava.version} org.apache.commons commons-lang3 3.17.0 commons-io commons-io 2.17.0 com.zaxxer HikariCP mysql mysql-connector-java com.microsoft.sqlserver mssql-jdbc 12.4.2.jre11 runtime org.postgresql postgresql runtime org.mariadb.jdbc mariadb-java-client runtime com.oracle.database.jdbc ojdbc11 runtime org.springframework spring-jdbc org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-integration org.springframework.retry spring-retry org.springframework.boot spring-boot-starter-validation org.springframework.boot spring-boot-devtools runtime org.springframework.boot spring-boot-configuration-processor true org.springframework.boot spring-boot-starter-test test org.junit.jupiter junit-jupiter-api test uk.org.webcompere system-stubs-jupiter 2.0.2 test org.testcontainers testcontainers ${testcontainers.version} test org.testcontainers junit-jupiter ${testcontainers.version} test org.testcontainers mysql ${testcontainers.version} test org.testcontainers mariadb ${testcontainers.version} test org.testcontainers mssqlserver ${testcontainers.version} test org.testcontainers postgresql ${testcontainers.version} test org.testcontainers oracle-xe ${testcontainers.version} test org.liquibase liquibase-core ${liquibase-core.version} test com.github.spullara.mustache.java compiler ${mustache.compiler.api.version} org.mapstruct mapstruct ${org.mapstruct.version} spring-boot:run src/test/resources/ org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-surefire-plugin ${maven.surefire.plugin.version} 2.5C false com.hubspot.maven.plugins prettier-maven-plugin 0.22 1.5.0 validate ${plugin.prettier.goal} org.apache.maven.plugins maven-checkstyle-plugin ${maven.checkstyle.plugin.version} com.puppycrawl.tools checkstyle ${checkstyle.version} checkstyle.xml checkstyle-suppressions.xml org.liquibase liquibase-maven-plugin ${liquibase-maven-plugin.version} true src/test/resources/liquibase.properties org.apache.maven.plugins maven-compiler-plugin 17 17 org.mapstruct mapstruct-processor ${org.mapstruct.version} spring-snapshots Spring Snapshots https://repo.spring.io/snapshot true spring-milestones Spring Milestones https://repo.spring.io/milestone false spring-snapshots Spring Snapshots https://repo.spring.io/snapshot true spring-milestones Spring Milestones https://repo.spring.io/milestone false ================================================ FILE: src/main/java/org/blackdread/sqltojava/SqlToJavaApplication.java ================================================ package org.blackdread.sqltojava; import java.util.List; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.entity.JdlEntity; import org.blackdread.sqltojava.service.logic.ExportService; import org.blackdread.sqltojava.service.logic.JdlService; import org.blackdread.sqltojava.service.logic.SqlService; import org.blackdread.sqltojava.util.AppUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.properties.EnableConfigurationProperties; @EnableConfigurationProperties(ApplicationProperties.class) @SpringBootApplication public class SqlToJavaApplication implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(SqlToJavaApplication.class); private final ApplicationProperties applicationProperties; private final SqlService sqlService; private final JdlService jdlService; private final ExportService exportService; public SqlToJavaApplication( final ApplicationProperties applicationProperties, final SqlService sqlService, final JdlService jdlService, final ExportService exportService ) { this.applicationProperties = applicationProperties; this.sqlService = sqlService; this.jdlService = jdlService; this.exportService = exportService; } public static void main(String[] args) { SpringApplication app = new SpringApplicationBuilder(SqlToJavaApplication.class).application(); AppUtil.setup(app).run(args); } @Override public void run(final String... args) throws Exception { log.warn("Arguments passed: {}", args); final List entities = jdlService.buildEntities(); exportService.export(entities); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/config/ApplicationProperties.java ================================================ package org.blackdread.sqltojava.config; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConstructorBinding; import org.springframework.boot.json.JsonParserFactory; import org.springframework.util.ResourceUtils; @ConstructorBinding @ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) public class ApplicationProperties { private static final Logger log = LoggerFactory.getLogger(ApplicationProperties.class); /** * Database name to export to JDL */ private final String databaseToExport; private final List databaseObjectPrefix; private final boolean addTableNameJdl; private final UndefinedJdlTypeHandlingEnum undefinedTypeHandling; private final DatabaseObjectTypesConfigEnum databaseObjectTypesConfig; private final Boolean renderEntitiesOnly; private final Boolean assumeBidirectional; private final List ignoredTableNames; private final Export export; private final List reservedList; private final Map jdlTypeOverrides; @SuppressWarnings("unchecked") public ApplicationProperties( final String databaseToExport, final List databaseObjectPrefix, final Boolean addTableNameJdl, final UndefinedJdlTypeHandlingEnum undefinedTypeHandling, final DatabaseObjectTypesConfigEnum databaseObjectTypesConfig, final Boolean renderEntitiesOnly, final Boolean assumeBidirectional, final List ignoredTableNames, final Export export, final String reservedKeywords, final Map jdlTypeOverrides ) { log.info("Loading ApplicationProperties..."); this.databaseToExport = databaseToExport; this.databaseObjectPrefix = databaseObjectPrefix; this.undefinedTypeHandling = undefinedTypeHandling; this.databaseObjectTypesConfig = databaseObjectTypesConfig; this.renderEntitiesOnly = renderEntitiesOnly; this.assumeBidirectional = assumeBidirectional; this.addTableNameJdl = Optional.of(addTableNameJdl).orElse(false); this.ignoredTableNames = ignoredTableNames; this.export = export; this.reservedList = JsonParserFactory .getJsonParser() .parseMap(keywordsAsJson(reservedKeywords)) .values() .stream() .map(obj -> (List) obj) .flatMap(Collection::stream) .collect(Collectors.toList()); this.jdlTypeOverrides = Optional.ofNullable(jdlTypeOverrides).orElse(Collections.emptyMap()); } public String getDatabaseToExport() { return databaseToExport; } public List getIgnoredTableNames() { return ignoredTableNames; } public Export getExport() { return export; } public List getReservedList() { return reservedList; } public Boolean getAddTableNameJdl() { return addTableNameJdl; } public UndefinedJdlTypeHandlingEnum getUndefinedTypeHandling() { return undefinedTypeHandling; } public DatabaseObjectTypesConfigEnum getDatabaseObjectTypesConfig() { return databaseObjectTypesConfig; } public Boolean isRenderEntitiesOnly() { return renderEntitiesOnly; } public Boolean isAssumeBidirectional() { return assumeBidirectional; } public List getDatabaseObjectPrefix() { return databaseObjectPrefix; } public Map getJdlTypeOverrides() { return jdlTypeOverrides; } public static class Export { private final Path path; private final String type; private final ExportFileStructureType exportFileStructureType; private final String exportMustacheTemplateFilenameOptional; public Export( final Path path, final String type, ExportFileStructureType exportFileStructureType, String exportMustacheTemplateFilenameOptional ) { this.path = path; this.type = type; this.exportFileStructureType = exportFileStructureType; this.exportMustacheTemplateFilenameOptional = exportMustacheTemplateFilenameOptional; } public Path getPath() { return path; } public String getType() { return type; } public ExportFileStructureType getExportFileStructureType() { return exportFileStructureType; } public String getExportMustacheTemplateFilenameOptional() { return exportMustacheTemplateFilenameOptional; } } private String keywordsAsJson(String file) { try { Path path = ResourceUtils.getFile(file).toPath(); return String.join(" ", Files.readAllLines(path)); } catch (IOException e) { return "{\"key\": []}"; } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/config/CacheConfiguration.java ================================================ package org.blackdread.sqltojava.config; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableCaching public class CacheConfiguration { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager(); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/config/DatabaseObjectTypesConfigEnum.java ================================================ package org.blackdread.sqltojava.config; public enum DatabaseObjectTypesConfigEnum { TABLES, VIEWS, ALL, } ================================================ FILE: src/main/java/org/blackdread/sqltojava/config/ExportFileStructureType.java ================================================ package org.blackdread.sqltojava.config; public enum ExportFileStructureType { SEPARATED("application", "Every entity, relationship are separated"), GROUPED_RELATIONS_SEPARATE_VIEWS( "application-grouped-relations-separate-views", "Relations are grouped by type, views are separated from entites after entites and relations declarations" ), RELATIONS_BEFORE_VIEWS_SEPARATE_VIEWS( "application-entities-relations-views", "Relations are after entities and before views, views are separated from entites after entites and relations declarations" ); private final String exportMustacheTemplateFilename; private final String comment; ExportFileStructureType(String exportMustacheTemplateFilename, String comment) { this.exportMustacheTemplateFilename = exportMustacheTemplateFilename; this.comment = comment; } public String getExportMustacheTemplateFilename() { return exportMustacheTemplateFilename; } public String getComment() { return comment; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/config/UndefinedJdlTypeHandlingEnum.java ================================================ package org.blackdread.sqltojava.config; /** * Determines how undefined SQL column types ar handled */ public enum UndefinedJdlTypeHandlingEnum { /** * Throw an error */ ERROR, /** * Skip writing the field to JDL */ SKIP, /** * Write Unsupported as type and require manual cleanup */ UNSUPPORTED, } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/JdlEntity.java ================================================ package org.blackdread.sqltojava.entity; import java.util.List; import java.util.Optional; public interface JdlEntity { String getName(); String getTableName(); List getFields(); /** * Add @readOnly to read only entity. * https://www.jhipster.tech/jdl/options */ boolean isReadOnly(); Optional getComment(); boolean isEnumEntity(); /** * A pure ManyToMany entity only contains 2 columns of foreign keys * * @return True if this entity represent a pure ManyToMany relation of two other entities */ boolean isPureManyToMany(); /** * @return Relations where this entity is the owner side */ List getRelations(); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/JdlField.java ================================================ package org.blackdread.sqltojava.entity; import java.util.Optional; public interface JdlField { JdlFieldEnum getType(); String getName(); /** * @return Entity name of the enum if this field is an enum value */ Optional getEnumEntityName(); /** * Some DB support native enum (no use of extra table as an enum). * If is true then {@link #getEnumEntityName()} is not empty. * * @return true if field was created from a native enum column */ boolean isNativeEnum(); boolean isRequired(); Optional getComment(); /** * Min of field (represent minLength if is a string) * * @return Min value of field */ Optional getMin(); /** * Max of field (represent miaxength if is a string) * * @return Max value of field */ Optional getMax(); Optional getPattern(); /** * @return - true if the column is marked as a unique column */ boolean isUnique(); /** * @return - true if the column is marked as a primary key column */ boolean isPrimaryKey(); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/JdlFieldEnum.java ================================================ package org.blackdread.sqltojava.entity; import com.google.common.base.CaseFormat; public enum JdlFieldEnum { STRING, INTEGER, LONG, BIG_DECIMAL, FLOAT, DOUBLE, ENUM, BOOLEAN, LOCAL_DATE, ZONED_DATE_TIME, INSTANT, BLOB, ANY_BLOB, IMAGE_BLOB, TEXT_BLOB, /** * Used for native enums from DBs, values are defined in the DB so can be extracted. * * @deprecated not sure yet, this should be checked via extra methods and not a type */ ENUM_NATIVE, /** * Defined here to allow to have a pattern set for TIME type of SQL that jdl does not support by default */ TIME_AS_TEXT, /** * Defined here to allow to have a pattern set for YEAR type of SQL that jdl does not support by default */ YEAR_AS_TEXT, /** * Defined here to allow to have a pattern set for GEOMETRY type of SQL that jdl does not support by default */ GEOMETRY_AS_TEXT, /** * Defined here to allow to have a string without max length set for json type of SQL that jdl does not support by default */ JSON_AS_TEXT, UUID, /** * Defined here to allow to have a string without max length set for postgres text type */ STRING_UNBOUNDED, /** * Defined here to allow to jdl to generate with unsupported fields rather than failing with an error. */ UNSUPPORTED; public String toCamelUpper() { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, this.name()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/JdlRelation.java ================================================ package org.blackdread.sqltojava.entity; import java.util.Optional; public interface JdlRelation { RelationType getRelationType(); boolean isBidirectional(); boolean isOwnerRequired(); boolean isInverseSideRequired(); // boolean isMapsId(); Optional getOwnerComment(); Optional getInverseSideComment(); /** * A comment that can be set to be displayed on top of the relation declaration, to help review the generated * * @return an extra comment */ Optional getComment(); /** * OwnerSideEntityName{ownerRelationName(ownerDisplayField) required} to InverseSideEntityName{inverseSideRelationName(inverseSideDisplayField)} * * @return Entity name of owner side */ String getOwnerEntityName(); /** * OwnerSideEntityName{ownerRelationName(ownerDisplayField) required} to InverseSideEntityName{inverseSideRelationName(inverseSideDisplayField)} * * @return Entity name of inverse side */ String getInverseSideEntityName(); /** * @return Represent the "relationship name" on the owner side */ String getOwnerRelationName(); /** * OwnerSideEntityName{ownerRelationName(ownerDisplayField) required} to InverseSideEntityName{inverseSideRelationName(inverseSideDisplayField)} * * @return Represent the "relationship name" on the inverse side */ Optional getInverseSideRelationName(); /** * OwnerSideEntityName{ownerRelationName(ownerDisplayField) required} to InverseSideEntityName{inverseSideRelationName(inverseSideDisplayField)} * * @return Represent the "display field name" on the owner side */ Optional getOwnerDisplayField(); /** * OwnerSideEntityName{ownerRelationName(ownerDisplayField) required} to InverseSideEntityName{inverseSideRelationName(inverseSideDisplayField)} * * @return Represent the "display field name" on the inverse side */ Optional getInverseSideDisplayField(); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/JdlRelationGroup.java ================================================ package org.blackdread.sqltojava.entity; import java.util.List; public interface JdlRelationGroup { RelationType getRelationType(); List getRelations(); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/RelationType.java ================================================ package org.blackdread.sqltojava.entity; public enum RelationType { /** * @deprecated no reason to keep that, exception way before should happen */ @Deprecated Unknown, /** * E.g: Can be known if sql column is unique */ OneToOne, ManyToOne, ManyToMany; // OneToMany is not put as we always use ManyToOne public String toJdl() { return this.name(); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/SqlColumn.java ================================================ package org.blackdread.sqltojava.entity; import java.util.Optional; public interface SqlColumn { SqlTable getTable(); /** * @return Column name */ String getName(); /** * Is usually a value of type and size like "type(size)" * * @return Column type */ String getType(); boolean isPrimaryKey(); boolean isForeignKey(); boolean isNullable(); boolean isUnique(); /** * Some DB support native enum (no use of extra table as an enum) * * @return true if column is a native enum */ boolean isNativeEnum(); Optional getDefaultValue(); Optional getComment(); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/SqlForeignKey.java ================================================ package org.blackdread.sqltojava.entity; public interface SqlForeignKey {} ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/SqlTable.java ================================================ package org.blackdread.sqltojava.entity; import java.util.Optional; public interface SqlTable { String getName(); Optional getComment(); boolean isUpdatable(); String getType(); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/impl/JdlEntityImpl.java ================================================ package org.blackdread.sqltojava.entity.impl; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Optional; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.entity.JdlEntity; import org.blackdread.sqltojava.entity.JdlField; import org.blackdread.sqltojava.entity.JdlRelation; @Immutable @ThreadSafe public class JdlEntityImpl implements JdlEntity, Comparable { private final String name; private final String tableName; private final List fields; private final String comment; private final boolean readOnly; private final boolean enumEntity; private final boolean pureManyToMany; private final List relations; public JdlEntityImpl( final String name, final String tableName, final List fields, @Nullable final String comment, final boolean enumEntity, final boolean readOnly, final boolean pureManyToMany, final List relations ) { this.name = name; this.tableName = tableName; this.fields = ImmutableList.copyOf(fields); this.comment = (StringUtils.isBlank(comment) || "null".equalsIgnoreCase(comment)) ? null : comment; this.enumEntity = enumEntity; this.readOnly = readOnly; this.pureManyToMany = pureManyToMany; this.relations = ImmutableList.copyOf(relations); } @Override public String getName() { return name; } @Override public String getTableName() { return tableName; } @Override public List getFields() { return fields; } @Override public Optional getComment() { return Optional.ofNullable(comment); } @Override public boolean isEnumEntity() { return enumEntity; } @Override public boolean isReadOnly() { return readOnly; } @Override public boolean isPureManyToMany() { return pureManyToMany; } @Override public List getRelations() { return relations; } @Override public String toString() { return ( "JdlEntityImpl{" + "name='" + name + '\'' + "tableName='" + tableName + '\'' + ", fields=" + fields + ", comment='" + comment + '\'' + ", readOnly=" + readOnly + ", isEnum=" + enumEntity + ", relations=" + relations + '}' ); } @Override public int compareTo(final JdlEntity o) { return name.compareTo(o.getName()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/impl/JdlFieldImpl.java ================================================ package org.blackdread.sqltojava.entity.impl; import java.util.Optional; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.entity.JdlField; import org.blackdread.sqltojava.entity.JdlFieldEnum; @Immutable @ThreadSafe public class JdlFieldImpl implements JdlField { private final JdlFieldEnum type; private final String name; private final String enumEntityName; private final boolean required; private final String comment; private final Integer min; private final Integer max; private final String pattern; private final Boolean nativeEnum; private final Boolean unique; private final Boolean primaryKey; public JdlFieldImpl( final JdlFieldEnum type, final String name, final boolean required, @Nullable final String comment, @Nullable final Integer min, @Nullable final Integer max, @Nullable final String pattern, @Nullable final String enumEntityName, final boolean nativeEnum, final boolean unique, final boolean primaryKey ) { this.type = type; this.name = name; this.required = required; this.comment = (StringUtils.isBlank(comment) || "null".equalsIgnoreCase(comment)) ? null : comment; this.min = min; this.max = max; this.pattern = pattern; this.enumEntityName = enumEntityName; this.nativeEnum = nativeEnum; this.unique = unique; this.primaryKey = primaryKey; } @Override public JdlFieldEnum getType() { return type; } @Override public String getName() { return name; } @Override public Optional getEnumEntityName() { return Optional.ofNullable(enumEntityName); } @Override public boolean isRequired() { return required; } @Override public Optional getComment() { return Optional.ofNullable(comment); } @Override public Optional getMin() { return Optional.ofNullable(min); } @Override public Optional getMax() { return Optional.ofNullable(max); } @Override public Optional getPattern() { return Optional.ofNullable(pattern); } @Override public boolean isUnique() { return unique; } @Override public boolean isPrimaryKey() { return primaryKey; } @Override public boolean isNativeEnum() { return nativeEnum; } @Override public String toString() { return ( "JdlFieldImpl{" + "type=" + type + ", name='" + name + '\'' + ", enumEntityName='" + enumEntityName + '\'' + ", isRequired=" + required + ", comment='" + comment + '\'' + ", min=" + min + ", max=" + max + ", pattern='" + pattern + '\'' + ", isNativeEnum=" + nativeEnum + '}' ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/impl/JdlRelationGroupImpl.java ================================================ package org.blackdread.sqltojava.entity.impl; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import org.blackdread.sqltojava.entity.JdlRelation; import org.blackdread.sqltojava.entity.JdlRelationGroup; import org.blackdread.sqltojava.entity.RelationType; @Immutable @ThreadSafe public class JdlRelationGroupImpl implements JdlRelationGroup, Comparable { private final RelationType relationType; private final List relations; public JdlRelationGroupImpl(RelationType relationType, List relations) { this.relationType = relationType; this.relations = relations; } public RelationType getRelationType() { return relationType; } public List getRelations() { return relations; } @Override public int compareTo(final JdlRelationGroup o) { return this.relationType.compareTo(o.getRelationType()); } @Override public String toString() { return ( "JdlRelationGroupImpl{" + "relationType=" + relationType + ", relations=" + relations.stream().map(e -> e.getOwnerEntityName()).collect(Collectors.joining()) + '}' ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/impl/JdlRelationImpl.java ================================================ package org.blackdread.sqltojava.entity.impl; import java.util.Optional; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.entity.JdlRelation; import org.blackdread.sqltojava.entity.RelationType; @Immutable @ThreadSafe public class JdlRelationImpl implements JdlRelation, Comparable { private final RelationType relationType; private final boolean bidirectional; private final boolean ownerRequired; private final boolean inverseSideRequired; private final String ownerEntityName; private final String inverseSideEntityName; private final String ownerRelationName; private final String ownerDisplayField; private final String ownerComment; private final String inverseSideComment; private final String inverseSideRelationName; private final String inverseSideDisplayField; private final String comment; public JdlRelationImpl( final RelationType relationType, final boolean bidirectional, final boolean ownerRequired, final boolean inverseSideRequired, final String ownerEntityName, final String inverseSideEntityName, final String ownerRelationName, @Nullable final String ownerDisplayField, @Nullable final String ownerComment, @Nullable final String inverseSideComment, @Nullable final String inverseSideRelationName, @Nullable final String inverseSideDisplayField, @Nullable final String comment ) { this.relationType = relationType; this.bidirectional = bidirectional; this.ownerRequired = ownerRequired; this.inverseSideRequired = inverseSideRequired; this.ownerEntityName = ownerEntityName; this.inverseSideEntityName = inverseSideEntityName; this.ownerRelationName = ownerRelationName; this.ownerDisplayField = (StringUtils.isBlank(ownerDisplayField) || "null".equalsIgnoreCase(ownerDisplayField)) ? null : ownerDisplayField; this.ownerComment = (StringUtils.isBlank(ownerComment) || "null".equalsIgnoreCase(ownerComment)) ? null : ownerComment; this.inverseSideComment = (StringUtils.isBlank(inverseSideComment) || "null".equalsIgnoreCase(inverseSideComment)) ? null : inverseSideComment; this.inverseSideRelationName = (StringUtils.isBlank(inverseSideRelationName) || "null".equalsIgnoreCase(inverseSideRelationName)) ? null : inverseSideRelationName; this.inverseSideDisplayField = (StringUtils.isBlank(inverseSideDisplayField) || "null".equalsIgnoreCase(inverseSideDisplayField)) ? null : inverseSideDisplayField; this.comment = (StringUtils.isBlank(comment) || "null".equalsIgnoreCase(comment)) ? null : comment; } @Override public RelationType getRelationType() { return relationType; } @Override public boolean isBidirectional() { return bidirectional; } @Override public boolean isOwnerRequired() { return ownerRequired; } @Override public boolean isInverseSideRequired() { return inverseSideRequired; } @Override public Optional getOwnerComment() { return Optional.ofNullable(ownerComment); } @Override public Optional getInverseSideComment() { return Optional.ofNullable(inverseSideComment); } @Override public Optional getComment() { return Optional.ofNullable(comment); } @Override public String getOwnerEntityName() { return ownerEntityName; } @Override public String getInverseSideEntityName() { return inverseSideEntityName; } @Override public String getOwnerRelationName() { return ownerRelationName; } @Override public Optional getInverseSideRelationName() { return Optional.ofNullable(inverseSideRelationName); } @Override public Optional getOwnerDisplayField() { return Optional.ofNullable(ownerDisplayField); } @Override public Optional getInverseSideDisplayField() { return Optional.ofNullable(inverseSideDisplayField); } @Override public int compareTo(final JdlRelation o) { return this.ownerEntityName.compareTo(o.getOwnerEntityName()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/impl/SqlColumnImpl.java ================================================ package org.blackdread.sqltojava.entity.impl; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.entity.SqlColumn; import org.blackdread.sqltojava.entity.SqlTable; public class SqlColumnImpl implements SqlColumn { private final SqlTable table; private final String name; private final String type; private final boolean isPrimaryKey; private final boolean isForeignKey; private final boolean isNullable; private final boolean isUnique; private final boolean isNativeEnum; private final String defaultValue; private final String comment; public SqlColumnImpl( final SqlTable table, final String name, final String type, final boolean isPrimaryKey, final boolean isForeignKey, final boolean isNullable, final boolean isUnique, final boolean isNativeEnum, @Nullable final String defaultValue, final String comment ) { this.table = table; this.name = name; this.type = type; this.isPrimaryKey = isPrimaryKey; this.isForeignKey = isForeignKey; this.isNullable = isNullable; this.isUnique = isUnique; this.isNativeEnum = isNativeEnum; this.defaultValue = (StringUtils.isBlank(defaultValue) || "null".equalsIgnoreCase(comment)) ? null : defaultValue; this.comment = (StringUtils.isBlank(comment) || "null".equalsIgnoreCase(comment)) ? null : comment; } @Override public SqlTable getTable() { return table; } @Override public String getName() { return name; } @Override public String getType() { return type; } @Override public boolean isPrimaryKey() { return isPrimaryKey; } @Override public boolean isForeignKey() { return isForeignKey; } @Override public boolean isNullable() { return isNullable; } @Override public boolean isUnique() { return isUnique; } @Override public boolean isNativeEnum() { return isNativeEnum; } @Override public Optional getDefaultValue() { return Optional.ofNullable(defaultValue); } @Override public Optional getComment() { return Optional.ofNullable(comment); } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final SqlColumnImpl sqlColumn = (SqlColumnImpl) o; return (Objects.equals(table, sqlColumn.table) && Objects.equals(name, sqlColumn.name) && Objects.equals(type, sqlColumn.type)); } @Override public int hashCode() { return Objects.hash(table, name, type); } @Override public String toString() { return ( "SqlColumnImpl{" + "table=" + table + ", name='" + name + '\'' + ", type='" + type + '\'' + ", isPrimaryKey=" + isPrimaryKey + ", isForeignKey=" + isForeignKey + ", isNullable=" + isNullable + ", isUnique=" + isUnique + ", isNativeEnum=" + isNativeEnum + ", defaultValue='" + defaultValue + '\'' + ", comment='" + comment + '\'' + '}' ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/entity/impl/SqlTableImpl.java ================================================ package org.blackdread.sqltojava.entity.impl; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.entity.SqlTable; public class SqlTableImpl implements SqlTable, Comparable { private final String name; private final String comment; private final boolean isUpdateable; private final String type; public SqlTableImpl(final String name, @Nullable final String comment, final String type, final boolean isUpdateable) { this.name = Objects.requireNonNull(name); this.comment = (StringUtils.isBlank(comment) || "null".equalsIgnoreCase(comment)) ? null : comment; this.type = type; this.isUpdateable = isUpdateable; } @Override public String getName() { return name; } @Override public Optional getComment() { return Optional.ofNullable(comment); } @Override public String getType() { return type; } @Override public boolean isUpdatable() { return isUpdateable; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final SqlTableImpl sqlTable = (SqlTableImpl) o; return Objects.equals(name, sqlTable.name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return ("SqlTableImpl{" + "name='" + name + '\'' + ", comment='" + comment + '\'' + '}'); } @Override public int compareTo(final SqlTable o) { return name.compareTo(o.getName()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/exporter/ExportFileStructureConfig.java ================================================ package org.blackdread.sqltojava.exporter; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.config.ExportFileStructureType; import org.springframework.stereotype.Service; @Service public class ExportFileStructureConfig { private final ApplicationProperties applicationProperties; private ExportFileStructureType exportFileStructureType; private String exportMustacheTemplateFilename; public ExportFileStructureConfig(ApplicationProperties applicationProperties) { this.applicationProperties = applicationProperties; exportFileStructureType = applicationProperties.getExport().getExportFileStructureType(); String newTemplateName = applicationProperties.getExport().getExportMustacheTemplateFilenameOptional(); exportMustacheTemplateFilename = StringUtils.isBlank(newTemplateName) ? exportFileStructureType.getExportMustacheTemplateFilename() : newTemplateName; } public String getExportMustacheTemplateFilename() { return exportMustacheTemplateFilename; } public ExportFileStructureType getExportFileStructureType() { return exportFileStructureType; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/listener/SetDatabaseProfileApplicationEventListener.java ================================================ package org.blackdread.sqltojava.listener; import org.blackdread.sqltojava.util.JdbcUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.env.ConfigurableEnvironment; public class SetDatabaseProfileApplicationEventListener implements ApplicationListener { private static final Logger log = LoggerFactory.getLogger(SetDatabaseProfileApplicationEventListener.class); @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent e) { ConfigurableEnvironment env = e.getEnvironment(); String[] activeProfiles = env.getActiveProfiles(); if (activeProfiles.length == 0) { String jdbcUrl = env.getProperty("spring.datasource.url"); String profileName = JdbcUtil.getDatabaseTypeFromJdbcUrl(jdbcUrl); log.debug(String.format("No active profiles using %s from %s", profileName, jdbcUrl)); env.setActiveProfiles(profileName); } else { log.debug(String.format("Active profile %s found. Automatic setting from spring.datasource.url disabled.", activeProfiles)); } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/parser/SqlParser.java ================================================ package org.blackdread.sqltojava.parser; import com.google.common.io.Files; import java.io.File; public class SqlParser { /** * @param fileToParse Sql file * @param saveInto Jdl file to save into */ public static void parse(final File fileToParse, final File saveInto) { checkIsSql(fileToParse); } private static void checkIsSql(final File file) { final String fileExtension = Files.getFileExtension(file.getName()); if (!fileExtension.equalsIgnoreCase("sql")) { throw new IllegalArgumentException("Not an sql file"); } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/ColumnInformation.java ================================================ package org.blackdread.sqltojava.pojo; import java.util.Optional; import org.apache.commons.lang3.StringUtils; public class ColumnInformation { private final String name; private final String type; private final boolean isNullable; private final boolean isPrimary; private final boolean isUnique; private final String defaultValue; private final int ordinalPosition; private final String comment; public ColumnInformation( final String name, final String type, final boolean isNullable, final boolean isPrimary, final boolean isUnique, final String defaultValue, final int ordinalPosition, final String comment ) { this.name = name; this.type = type; this.isNullable = isNullable; this.isPrimary = isPrimary; this.isUnique = isUnique; if (StringUtils.isBlank(defaultValue) || "null".equalsIgnoreCase(defaultValue)) { this.defaultValue = null; } else { this.defaultValue = defaultValue; } this.ordinalPosition = ordinalPosition; this.comment = comment; } public ColumnInformation( final String name, final String type, final String nullValue, final String keyValue, final String defaultValue, int ordinalPosition, final String comment ) { this( name, type, "yes".equalsIgnoreCase(nullValue), "pri".equalsIgnoreCase(keyValue), "uni".equalsIgnoreCase(keyValue), defaultValue, ordinalPosition, comment ); } public String getName() { return name; } public String getType() { return type; } public boolean isNullable() { return isNullable; } public boolean isPrimary() { return isPrimary; } public boolean isUnique() { return isUnique; } public Optional getDefaultValue() { return Optional.ofNullable(defaultValue); } public int getOrdinalPosition() { return ordinalPosition; } public String getComment() { return comment; } @Override public String toString() { return ( "ColumnInformation{" + "name='" + name + '\'' + ", type='" + type + '\'' + ", isNullable=" + isNullable + ", isPrimary=" + isPrimary + ", isUnique=" + isUnique + ", defaultValue='" + defaultValue + '\'' + ", comment='" + comment + '\'' + '}' ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/TableInformation.java ================================================ package org.blackdread.sqltojava.pojo; import java.util.Objects; import java.util.Optional; import org.apache.commons.lang3.StringUtils; public class TableInformation { private String schema; private final String name; private final String comment; private final boolean isUpdateable; private final String type; public TableInformation(final String name, final boolean isUpdateable, String type, final String comment) { this.name = name; this.isUpdateable = isUpdateable; this.comment = (StringUtils.isBlank(comment) || "null".equalsIgnoreCase(comment)) ? null : comment; this.type = type; } public String getName() { return name; } public Optional getComment() { return Optional.ofNullable(comment); } public String getSchema() { return schema; } public Boolean isUpdateable() { return isUpdateable; } public String getType() { return type; } @Override public String toString() { return ("TableInformation{" + "name='" + name + '\'' + ", comment='" + comment + '\'' + '}'); } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final TableInformation that = (TableInformation) o; return Objects.equals(name, that.name); } @Override public int hashCode() { return name.hashCode(); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/TableRelationInformation.java ================================================ package org.blackdread.sqltojava.pojo; import java.util.Objects; public class TableRelationInformation { private final String tableName; private final String columnName; private final String referencedTableName; private final String referencedColumnName; public TableRelationInformation( final String tableName, final String columnName, final String referencedTableName, final String referencedColumnName ) { this.tableName = tableName; this.columnName = columnName; this.referencedTableName = referencedTableName; this.referencedColumnName = referencedColumnName; } public String getTableName() { return tableName; } public String getColumnName() { return columnName; } public String getReferencedTableName() { return referencedTableName; } public String getReferencedColumnName() { return referencedColumnName; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final TableRelationInformation that = (TableRelationInformation) o; return ( Objects.equals(tableName, that.tableName) && Objects.equals(columnName, that.columnName) && Objects.equals(referencedTableName, that.referencedTableName) && Objects.equals(referencedColumnName, that.referencedColumnName) ); } @Override public int hashCode() { return Objects.hash(tableName, columnName, referencedTableName, referencedColumnName); } @Override public String toString() { return ( "TableRelationInformation{" + "tableName='" + tableName + '\'' + ", columnName='" + columnName + '\'' + ", referencedTableName='" + referencedTableName + '\'' + ", referencedColumnName='" + referencedColumnName + '\'' + '}' ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/rowmaper/ColumnInformationRowMapper.java ================================================ package org.blackdread.sqltojava.pojo.rowmaper; import java.sql.ResultSet; import java.sql.SQLException; import org.blackdread.sqltojava.pojo.ColumnInformation; import org.springframework.jdbc.core.RowMapper; public class ColumnInformationRowMapper implements RowMapper { @Override public ColumnInformation mapRow(ResultSet rs, int rowNum) throws SQLException { return new ColumnInformation( rs.getString("column_name"), rs.getString("data_type"), rs.getString("is_nullable"), rs.getString("key"), rs.getString("column_default"), rs.getInt("ordinal_position"), rs.getString("comment") ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/rowmaper/SqlServerColumnInformationRowMapper.java ================================================ package org.blackdread.sqltojava.pojo.rowmaper; import java.sql.ResultSet; import java.sql.SQLException; import org.blackdread.sqltojava.pojo.ColumnInformation; import org.springframework.jdbc.core.RowMapper; public class SqlServerColumnInformationRowMapper implements RowMapper { @Override public ColumnInformation mapRow(ResultSet rs, int rowNum) throws SQLException { return new ColumnInformation( rs.getString("column_name"), rs.getString("data_type"), rs.getString("is_nullable"), rs.getString("keye"), rs.getString("column_default"), rs.getInt("ordinal_position"), "" ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/rowmaper/SqlServerTableInformationRowMapper.java ================================================ package org.blackdread.sqltojava.pojo.rowmaper; import java.sql.ResultSet; import java.sql.SQLException; import org.blackdread.sqltojava.pojo.TableInformation; import org.springframework.jdbc.core.RowMapper; public class SqlServerTableInformationRowMapper implements RowMapper { @Override public TableInformation mapRow(ResultSet rs, int rowNum) throws SQLException { return new TableInformation( rs.getString("table_name"), rs.getBoolean("updateable"), rs.getString("table_type"), rs.getString("comment") ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/rowmaper/TableInformationRowMapper.java ================================================ package org.blackdread.sqltojava.pojo.rowmaper; import java.sql.ResultSet; import java.sql.SQLException; import org.blackdread.sqltojava.pojo.TableInformation; import org.springframework.jdbc.core.RowMapper; public class TableInformationRowMapper implements RowMapper { @Override public TableInformation mapRow(ResultSet rs, int rowNum) throws SQLException { return new TableInformation( rs.getString("table_name"), rs.getBoolean("is_updatable"), rs.getString("table_type"), rs.getString("comment") ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/pojo/rowmaper/TableRelationInformationRowMapper.java ================================================ package org.blackdread.sqltojava.pojo.rowmaper; import java.sql.ResultSet; import java.sql.SQLException; import org.blackdread.sqltojava.pojo.TableRelationInformation; import org.springframework.jdbc.core.RowMapper; public class TableRelationInformationRowMapper implements RowMapper { @Override public TableRelationInformation mapRow(ResultSet rs, int rowNum) throws SQLException { return new TableRelationInformation( rs.getString("table_name"), rs.getString("column_name"), rs.getString("foreign_table_name"), rs.getString("foreign_column_name") ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/repository/InformationSchemaRepository.java ================================================ package org.blackdread.sqltojava.repository; import java.util.List; import org.blackdread.sqltojava.pojo.ColumnInformation; import org.blackdread.sqltojava.pojo.TableInformation; import org.blackdread.sqltojava.pojo.TableRelationInformation; public interface InformationSchemaRepository { List getAllTableRelationInformation(final String dbName); List getFullColumnInformationOfTable(final String dbName, final String tableName); List getAllTableInformation(final String dbName); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/repository/MsSqlPureSqlInformationSchemaRepository.java ================================================ package org.blackdread.sqltojava.repository; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.blackdread.sqltojava.pojo.ColumnInformation; import org.blackdread.sqltojava.pojo.TableInformation; import org.blackdread.sqltojava.pojo.TableRelationInformation; import org.blackdread.sqltojava.pojo.rowmaper.SqlServerColumnInformationRowMapper; import org.blackdread.sqltojava.pojo.rowmaper.SqlServerTableInformationRowMapper; import org.blackdread.sqltojava.pojo.rowmaper.TableRelationInformationRowMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; @Repository @Profile({ "sqlserver" }) public class MsSqlPureSqlInformationSchemaRepository implements InformationSchemaRepository { private static final Logger log = LoggerFactory.getLogger(MsSqlPureSqlInformationSchemaRepository.class); private static String ALL_TABLE_RELATIONAL_INFROMATION; private static String FULL_COLUMN_INFORMATION_OF_TABLE; private static String ALL_TABLE_INFORMATION; private static final SqlServerTableInformationRowMapper TABLE_INFORMATION_MAPPER = new SqlServerTableInformationRowMapper(); private final NamedParameterJdbcTemplate template; @Autowired public MsSqlPureSqlInformationSchemaRepository(NamedParameterJdbcTemplate template, ConfigurableEnvironment env) { String activeProfile = env.getActiveProfiles()[0]; ALL_TABLE_RELATIONAL_INFROMATION = readSqlFileByProfile(activeProfile, "getAllTableRelationInformation"); FULL_COLUMN_INFORMATION_OF_TABLE = readSqlFileByProfile(activeProfile, "getFullColumnInformationOfTable"); ALL_TABLE_INFORMATION = readSqlFileByProfile(activeProfile, "getAllTableInformation"); this.template = template; } @Override public List getAllTableRelationInformation(final String schemaName) { Map paramMap = Map.of("schemaName", schemaName); return template.queryForStream(ALL_TABLE_RELATIONAL_INFROMATION, paramMap, new TableRelationInformationRowMapper()).toList(); } @Override public List getFullColumnInformationOfTable(final String schemaName, final String tableName) { Map paramMap = Map.of("schemaName", schemaName, "tableName", tableName); return template.queryForStream(FULL_COLUMN_INFORMATION_OF_TABLE, paramMap, new SqlServerColumnInformationRowMapper()).toList(); } public List getAllTableInformation(final String schemaName) { Map paramMap = Map.of("schemaName", schemaName); return template.queryForStream(ALL_TABLE_INFORMATION, paramMap, TABLE_INFORMATION_MAPPER).toList(); } /** * Loads a sql query file based on the active profile. * For multiple profiles to use the same sql files they should be named as follows: * sql/{profile1}_{profile1}-{queryName}.sql * See mysql_mariadb-queryName.sql for an example. * @param activeProfile * @param queryName * @return */ private String readSqlFileByProfile(String activeProfile, String queryName) { ClassLoader cl = this.getClass().getClassLoader(); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); String pattern = String.format("classpath*:/sql/*%s*-%s.sql", activeProfile, queryName); log.info("Using pattern " + pattern); try { Resource[] resources = resolver.getResources(pattern); if (resources.length != 1) { throw new IllegalArgumentException(String.format("Found %s when 1 was expected", resources.length)); } Resource r = resources[0]; log.info("Found resource " + r.getFilename()); Resource resource = new ClassPathResource("sql/" + r.getFilename()); InputStream inputStream = resource.getInputStream(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { return reader.lines().collect(Collectors.joining("\n")); } catch (IOException e) { throw new RuntimeException(e); } // return ResourceUtil.readString(r); } catch (IOException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/repository/PureSqlInformationSchemaRepository.java ================================================ package org.blackdread.sqltojava.repository; import java.io.IOException; import java.util.List; import java.util.Map; import org.blackdread.sqltojava.pojo.ColumnInformation; import org.blackdread.sqltojava.pojo.TableInformation; import org.blackdread.sqltojava.pojo.TableRelationInformation; import org.blackdread.sqltojava.pojo.rowmaper.ColumnInformationRowMapper; import org.blackdread.sqltojava.pojo.rowmaper.TableInformationRowMapper; import org.blackdread.sqltojava.pojo.rowmaper.TableRelationInformationRowMapper; import org.blackdread.sqltojava.util.ResourceUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; @Repository @Profile({ "mysql", "mariadb", "postgresql", "oracle" }) public class PureSqlInformationSchemaRepository implements InformationSchemaRepository { private static final Logger log = LoggerFactory.getLogger(PureSqlInformationSchemaRepository.class); private static String ALL_TABLE_RELATIONAL_INFROMATION; private static String FULL_COLUMN_INFORMATION_OF_TABLE; private static String ALL_TABLE_INFORMATION; private static final TableInformationRowMapper TABLE_INFORMATION_MAPPER = new TableInformationRowMapper(); private final NamedParameterJdbcTemplate template; @Autowired public PureSqlInformationSchemaRepository(NamedParameterJdbcTemplate template, ConfigurableEnvironment env) { String activeProfile = env.getActiveProfiles()[0]; ALL_TABLE_RELATIONAL_INFROMATION = readSqlFileByProfile(activeProfile, "getAllTableRelationInformation"); FULL_COLUMN_INFORMATION_OF_TABLE = readSqlFileByProfile(activeProfile, "getFullColumnInformationOfTable"); ALL_TABLE_INFORMATION = readSqlFileByProfile(activeProfile, "getAllTableInformation"); this.template = template; } @Override public List getAllTableRelationInformation(final String schemaName) { Map paramMap = Map.of("schemaName", schemaName); return template.queryForStream(ALL_TABLE_RELATIONAL_INFROMATION, paramMap, new TableRelationInformationRowMapper()).toList(); } @Override public List getFullColumnInformationOfTable(final String schemaName, final String tableName) { Map paramMap = Map.of("schemaName", schemaName, "tableName", tableName); return template.queryForStream(FULL_COLUMN_INFORMATION_OF_TABLE, paramMap, new ColumnInformationRowMapper()).toList(); } public List getAllTableInformation(final String schemaName) { Map paramMap = Map.of("schemaName", schemaName); return template.queryForStream(ALL_TABLE_INFORMATION, paramMap, TABLE_INFORMATION_MAPPER).toList(); } /** * Loads a sql query file based on the active profile. * For multiple profiles to use the same sql files they should be named as follows: * sql/{profile1}_{profile1}-{queryName}.sql * See mysql_mariadb-queryName.sql for an example. * @param activeProfile * @param queryName * @return */ private String readSqlFileByProfile(String activeProfile, String queryName) { ClassLoader cl = this.getClass().getClassLoader(); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); String pattern = String.format("classpath*:/sql/*%s*-%s.sql", activeProfile, queryName); log.info("Using pattern " + pattern); try { Resource[] resources = resolver.getResources(pattern); if (resources.length != 1) { throw new IllegalArgumentException(String.format("Found %s when 1 was expected", resources.length)); } Resource r = resources[0]; log.info("Found resource " + r.getFilename()); return ResourceUtil.readString(r); } catch (IOException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/InformationSchemaService.java ================================================ package org.blackdread.sqltojava.service; import java.util.List; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.pojo.ColumnInformation; import org.blackdread.sqltojava.pojo.TableInformation; import org.blackdread.sqltojava.pojo.TableRelationInformation; import org.blackdread.sqltojava.repository.InformationSchemaRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Transactional(readOnly = true) public class InformationSchemaService { private static final Logger log = LoggerFactory.getLogger(InformationSchemaService.class); private final ApplicationProperties applicationProperties; private final InformationSchemaRepository informationSchemaRepository; public InformationSchemaService( final ApplicationProperties applicationProperties, final InformationSchemaRepository informationSchemaRepository ) { this.applicationProperties = applicationProperties; this.informationSchemaRepository = informationSchemaRepository; } @Cacheable("InformationSchemaService.getAllTableRelationInformation") public List getAllTableRelationInformation() { log.debug("getAllTableRelationInformation called"); return informationSchemaRepository.getAllTableRelationInformation(applicationProperties.getDatabaseToExport()); } @Cacheable("InformationSchemaService.getFullColumnInformationOfTable") public List getFullColumnInformationOfTable(final String tableName) { log.debug("getFullColumnInformationOfTable called for table: {}", tableName); return informationSchemaRepository.getFullColumnInformationOfTable(applicationProperties.getDatabaseToExport(), tableName); } @Cacheable("InformationSchemaService.getAllTableInformation") public List getAllTableInformation() { log.debug("getAllTableInformation called"); return informationSchemaRepository.getAllTableInformation(applicationProperties.getDatabaseToExport()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/MsSqlJdlTypeService.java ================================================ package org.blackdread.sqltojava.service; import static java.util.Map.entry; import static org.blackdread.sqltojava.entity.JdlFieldEnum.*; import static org.blackdread.sqltojava.entity.JdlFieldEnum.UUID; import static org.blackdread.sqltojava.util.SqlUtils.COLUMN_TYPE_SIZE_REGEX; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.blackdread.sqltojava.entity.SqlColumn; import org.blackdread.sqltojava.util.SqlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @Service @Profile("sqlserver") public class MsSqlJdlTypeService implements SqlJdlTypeService { private static final Logger log = LoggerFactory.getLogger(MsSqlJdlTypeService.class); public static final int VARCHAR_MAX_LENGTH = 8000; public static final int NVARCHAR_MAX_LENGTH = 4000; public static final int COLUMN_TYPE_MAX_LENGTH_MAX_VALUE = -1; private final ApplicationProperties properties; public MsSqlJdlTypeService(ApplicationProperties properties) { this.properties = properties; } private final Map typeMap = Map.ofEntries( entry("bit", BOOLEAN), entry("date", LOCAL_DATE), entry("time", TIME_AS_TEXT), entry("datetime", INSTANT), entry("datetime2", INSTANT), entry("smalldatetime", INSTANT), entry("datetimeoffset", ZONED_DATE_TIME), entry("real", FLOAT), entry("float", DOUBLE), entry("smallint", INTEGER), entry("int", INTEGER), entry("bigint", LONG), entry("tinyint", INTEGER), entry("money", BIG_DECIMAL), entry("numeric", BIG_DECIMAL), entry("decimal", BIG_DECIMAL), entry("char", STRING), //char(max) is string with length up to 8000 entry("varchar", STRING), //varchar(max) is string with length up to 8000 entry("nvarchar", STRING), //nvarchar(max) is string with length up to 4000 entry("nchar", STRING), //nchar(max) is string with length up to 4000 entry("text", STRING_UNBOUNDED), entry("ntext", STRING_UNBOUNDED), // Added conversion for ntext (deprecated sql server type) entry("binary", BLOB), entry("varbinary", BLOB), entry("image", IMAGE_BLOB), entry("varbinary(max)", BLOB), entry("varchar(max)", TEXT_BLOB), entry("nvarchar(max)", TEXT_BLOB), entry("uniqueidentifier", UUID) ); @Override public Map getTypeMap() { return mergeOverrides(this.typeMap, properties.getJdlTypeOverrides()); } @Override public JdlFieldEnum sqlTypeToJdlType(String sqlType) { sqlType = StringUtils.trim(sqlType); final Matcher matcher = COLUMN_TYPE_SIZE_REGEX.matcher(sqlType); String typeName = null; if (matcher.matches()) { Integer max = matcher.group(3) == null ? null : Integer.valueOf(matcher.group(3)); typeName = matcher.group(1).toLowerCase(); if (ObjectUtils.isNotEmpty(max)) { if (max == COLUMN_TYPE_MAX_LENGTH_MAX_VALUE && typeName.equalsIgnoreCase("varbinary")) { typeName = "varbinary(max)".toLowerCase(); } else if (max == COLUMN_TYPE_MAX_LENGTH_MAX_VALUE && typeName.equalsIgnoreCase("varchar")) { typeName = "varchar(max)".toLowerCase(); } else if (max == COLUMN_TYPE_MAX_LENGTH_MAX_VALUE && typeName.equalsIgnoreCase("nvarchar")) { typeName = "nvarchar(max)".toLowerCase(); } } } return Optional.ofNullable(getTypeMap().get(typeName)).orElse(UNSUPPORTED); } @Override public Integer calculateStringMaxLength(SqlColumn column) { return SqlUtils.parseSqlSize(column.getType()).orElse(1); // String columnType = SqlUtils.parseSqlType(column.getType()); // if ("VARCHAR".equalsIgnoreCase(columnType) && max == COLUMN_TYPE_MAX_VALUE) { // return VARCHAR_MAX_LENGTH; // } // if ("NVARCHAR".equalsIgnoreCase(columnType) && max == COLUMN_TYPE_MAX_VALUE) { // return NVARCHAR_MAX_LENGTH; // } // return SqlJdlTypeService.super.calculateStringMaxLength(column); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/MySqlJdlTypeService.java ================================================ package org.blackdread.sqltojava.service; import static java.util.Map.entry; import static org.blackdread.sqltojava.entity.JdlFieldEnum.*; import java.util.Map; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @Service @Profile({ "mysql", "mariadb" }) public class MySqlJdlTypeService implements SqlJdlTypeService { private static final Logger log = LoggerFactory.getLogger(MySqlJdlTypeService.class); private final ApplicationProperties properties; public MySqlJdlTypeService(ApplicationProperties properties) { this.properties = properties; } private final Map typeMap = Map.ofEntries( entry("varchar", STRING), entry("char", STRING), entry("text", STRING), entry("tinytext", STRING), entry("mediumtext", TEXT_BLOB), entry("longtext", TEXT_BLOB), entry("tinyblob", BLOB), entry("blob", BLOB), entry("mediumblob", BLOB), entry("longblob", BLOB), entry("smallint", INTEGER), entry("mediumint", INTEGER), entry("int", INTEGER), entry("bigint", LONG), entry("float", FLOAT), entry("double", DOUBLE), entry("date", LOCAL_DATE), entry("enum", ENUM), entry("set", ENUM), entry("boolean", BOOLEAN), entry("tinyint", BOOLEAN), entry("bit", BOOLEAN), entry("decimal", BIG_DECIMAL), entry("datetime", INSTANT), entry("timestamp", INSTANT), /** * Not support by base jhipster but to export database which has this type. * See: * https://blog.ippon.fr/2017/12/04/la-technologie-spatiale-au-service-de-jhipster/ * https://github.com/chegola/jhipster-spatial * https://stackoverflow.com/questions/50122390/integration-of-postgis-with-jhipster **/ entry("geometry", GEOMETRY_AS_TEXT), /** * Not support by base jhipster but to export database which has this type. */ entry("json", JSON_AS_TEXT), entry("time", TIME_AS_TEXT), entry("year", YEAR_AS_TEXT) ); @Override public Map getTypeMap() { return mergeOverrides(this.typeMap, properties.getJdlTypeOverrides()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/OracleJdlTypeService.java ================================================ package org.blackdread.sqltojava.service; import static java.util.Map.entry; import static org.blackdread.sqltojava.entity.JdlFieldEnum.*; import java.util.Map; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @Service @Profile("oracle") public class OracleJdlTypeService implements SqlJdlTypeService { private static final Logger log = LoggerFactory.getLogger(OracleJdlTypeService.class); private final ApplicationProperties properties; public OracleJdlTypeService(ApplicationProperties properties) { this.properties = properties; } private final Map typeMap = Map.ofEntries( entry("VARCHAR2", STRING), entry("NUMBER", INTEGER), entry("NUMBER(38)", LONG), entry("NUMBER(19,5)", BIG_DECIMAL), entry("FLOAT", FLOAT), entry("BLOB", BLOB), entry("TIMESTAMP", INSTANT), entry("DATE", LOCAL_DATE), entry("CHAR", STRING), entry("CLOB", BLOB), entry("NCHAR", STRING), entry("NVARCHAR2", STRING), entry("RAW", UUID) ); @Override public Map getTypeMap() { return this.mergeOverrides(this.typeMap, properties.getJdlTypeOverrides()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/PostgresJdlTypeService.java ================================================ package org.blackdread.sqltojava.service; import static java.util.Map.entry; import static org.blackdread.sqltojava.entity.JdlFieldEnum.*; import java.util.Map; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @Service @Profile("postgresql") public class PostgresJdlTypeService implements SqlJdlTypeService { private static final Logger log = LoggerFactory.getLogger(PostgresJdlTypeService.class); private final ApplicationProperties properties; public PostgresJdlTypeService(ApplicationProperties properties) { this.properties = properties; } private final Map typeMap = Map.ofEntries( entry("boolean", BOOLEAN), entry("date", LOCAL_DATE), entry("time without time zone", TIME_AS_TEXT), entry("time with time zone", TIME_AS_TEXT), entry("timestamp without time zone", INSTANT), entry("timestamp with time zone", ZONED_DATE_TIME), entry("real", FLOAT), entry("double precision", DOUBLE), entry("smallint", INTEGER), entry("integer", INTEGER), entry("bigint", LONG), entry("money", BIG_DECIMAL), entry("numeric", BIG_DECIMAL), entry("character", STRING), entry("character varying", STRING), entry("text", STRING_UNBOUNDED), entry("bytea", BLOB), entry("json", JSON_AS_TEXT), entry("uuid", UUID) //Map.entry("interval", null), //Map.entry("jsonb", null), //Map.entry("jsonpath", null), //Map.entry("macaddr", STRING), //Map.entry("macaddr8", STRING), //Map.entry("xml", STRING) ); @Override public Map getTypeMap() { return mergeOverrides(this.typeMap, properties.getJdlTypeOverrides()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/SqlJdlTypeService.java ================================================ package org.blackdread.sqltojava.service; import static org.blackdread.sqltojava.entity.JdlFieldEnum.UNSUPPORTED; import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.blackdread.sqltojava.entity.SqlColumn; import org.blackdread.sqltojava.util.SqlUtils; public interface SqlJdlTypeService { Map getTypeMap(); default JdlFieldEnum sqlTypeToJdlType(final String sqlType) { String typeName = SqlUtils.parseSqlType(sqlType); JdlFieldEnum jdlType = Optional.ofNullable(getTypeMap().get(typeName)).orElse(UNSUPPORTED); //orElseThrow(() -> new IllegalStateException("Unknown type: " + typeName)); return jdlType; } default Map mergeOverrides( final Map defaultTypeMap, final Map overrides ) { Map typeMap = new HashMap<>(defaultTypeMap); overrides.forEach((k, v) -> typeMap.merge(k, v, (v1, v2) -> v2)); return ImmutableMap.copyOf(typeMap); } default Integer calculateStringMaxLength(SqlColumn column) { return SqlUtils.parseSqlSize(column.getType()).orElse(255); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/logic/ExportService.java ================================================ package org.blackdread.sqltojava.service.logic; import static java.util.Map.entry; import static java.util.Map.ofEntries; import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Collections; import java.util.List; import java.util.Map; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.config.ExportFileStructureType; import org.blackdread.sqltojava.entity.JdlEntity; import org.blackdread.sqltojava.exporter.ExportFileStructureConfig; import org.blackdread.sqltojava.util.JdlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class ExportService { private static final Logger log = LoggerFactory.getLogger(ExportService.class); private final ApplicationProperties properties; private final ExportFileStructureConfig exportFileStructureConfig; private final JdlService jdlService; private final MustacheService mustacheService; public ExportService( final ApplicationProperties properties, ExportFileStructureConfig exportFileStructureConfig, JdlService jdlService, MustacheService mustacheService ) { this.properties = properties; this.exportFileStructureConfig = exportFileStructureConfig; this.jdlService = jdlService; this.mustacheService = mustacheService; } /** * Exports jdl to string using mustache.java * @param entities * @return */ public String exportString(final List entities) { return mustacheService.executeTemplate( exportFileStructureConfig.getExportMustacheTemplateFilename(), resolveExportStructure(entities) ); } public Map resolveExportStructure(final List entities) { if (ExportFileStructureType.SEPARATED.equals(exportFileStructureConfig.getExportFileStructureType())) { return ofEntries( entry("entities", entities), entry("relations", jdlService.getRelations(entities)), entry("options", !properties.isRenderEntitiesOnly() ? JdlUtils.getOptions() : Collections.emptyList()) ); } else if ( ExportFileStructureType.GROUPED_RELATIONS_SEPARATE_VIEWS.equals(exportFileStructureConfig.getExportFileStructureType()) ) { List tables = extractTables(entities); List views = extractViews(entities); return ofEntries( entry("entities", tables), entry("groupedonetoonerelations", jdlService.getGroupOneToOneRelations(tables)), entry("groupedmanytoonerelations", jdlService.getGroupManyToOneRelations(tables)), entry("manytomanyrelations", jdlService.getManyToManyRelations(tables)), entry("views", views), entry("viewsrelations", jdlService.getRelations(views)), entry("options", !properties.isRenderEntitiesOnly() ? JdlUtils.getOptions() : Collections.emptyList()) ); } else if ( ExportFileStructureType.RELATIONS_BEFORE_VIEWS_SEPARATE_VIEWS.equals(exportFileStructureConfig.getExportFileStructureType()) ) { List tables = extractTables(entities); List views = extractViews(entities); return ofEntries( entry("entities", tables), entry("onetoonerelations", jdlService.getOneToOneRelations(tables)), entry("manytoonerelations", jdlService.getManyToOneRelations(tables)), entry("manytomanyrelations", jdlService.getManyToManyRelations(tables)), entry("views", views), entry("viewsrelations", jdlService.getRelations(views)), entry("options", !properties.isRenderEntitiesOnly() ? JdlUtils.getOptions() : Collections.emptyList()) ); } throw new IllegalStateException("Unknown export file structure type"); } private static List extractViews(List entities) { return entities.stream().filter(JdlEntity::isReadOnly).toList(); } private static List extractTables(List entities) { return entities.stream().filter(e -> !e.isReadOnly()).toList(); } /** * Exports jdl to file defined with export.path * @param entities * @return */ public void export(final List entities) { final String jdl = exportString(entities); final Path path = properties.getExport().getPath(); log.info("Exporting into: {}", path.toAbsolutePath()); if (Files.isDirectory(path)) { log.error("Path is a directory: {}", path.toAbsolutePath()); throw new IllegalArgumentException("Cannot export into a directory"); } try { Files.deleteIfExists(path); } catch (IOException e) { log.error("An error occurred when deleting export file"); throw new IllegalStateException(e); } try (BufferedWriter out = Files.newBufferedWriter(path, StandardOpenOption.CREATE)) { out.write(jdl); out.flush(); } catch (IOException e) { log.error("Error", e); throw new IllegalStateException(e); } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/logic/JdlService.java ================================================ package org.blackdread.sqltojava.service.logic; import static org.blackdread.sqltojava.entity.JdlFieldEnum.*; import static org.blackdread.sqltojava.util.NamingConventionUtil.*; import java.util.*; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.entity.*; import org.blackdread.sqltojava.entity.impl.JdlEntityImpl; import org.blackdread.sqltojava.entity.impl.JdlFieldImpl; import org.blackdread.sqltojava.entity.impl.JdlRelationGroupImpl; import org.blackdread.sqltojava.entity.impl.JdlRelationImpl; import org.blackdread.sqltojava.service.SqlJdlTypeService; import org.blackdread.sqltojava.util.JdlUtils; import org.blackdread.sqltojava.util.SqlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class JdlService { private static final Logger log = LoggerFactory.getLogger(JdlService.class); private final SqlService sqlService; private final SqlJdlTypeService sqlJdlTypeService; private final ApplicationProperties properties; public JdlService(final SqlService sqlService, final ApplicationProperties properties, final SqlJdlTypeService sqlJdlTypeService) { this.sqlService = sqlService; this.sqlJdlTypeService = sqlJdlTypeService; this.properties = properties; } public List buildEntities() { final List sqlColumns = sqlService.buildColumns(); // todo build entities for columns of native enums so we can later export to JDL the native enum and its values return SqlUtils .groupColumnsByTable(sqlColumns) .entrySet() .stream() .map(this::buildEntity) .filter(Optional::isPresent) .map(Optional::get) .sorted() .collect(Collectors.toList()); } public List getRelations(List entities) { return entities.stream().flatMap(e -> e.getRelations().stream()).collect(Collectors.toList()); } public List getOneToOneRelations(List entities) { return entities .stream() .flatMap(e -> e.getRelations().stream()) .filter(f -> RelationType.OneToOne.equals(f.getRelationType())) .collect(Collectors.toList()); } public List getManyToOneRelations(List entities) { return entities .stream() .flatMap(e -> e.getRelations().stream()) .filter(f -> RelationType.ManyToOne.equals(f.getRelationType())) .collect(Collectors.toList()); } public JdlRelationGroupImpl getGroupManyToOneRelations(List entities) { List idRelations = entities .stream() .flatMap(e -> e.getRelations().stream()) .filter(f -> RelationType.ManyToOne.equals(f.getRelationType())) .collect(Collectors.toList()); return new JdlRelationGroupImpl(RelationType.ManyToOne, idRelations); } public JdlRelationGroupImpl getGroupOneToOneRelations(List entities) { List idRelations = entities .stream() .flatMap(e -> e.getRelations().stream()) .filter(f -> RelationType.OneToOne.equals(f.getRelationType())) .collect(Collectors.toList()); return new JdlRelationGroupImpl(RelationType.OneToOne, idRelations); } public List getManyToManyRelations(List entities) { return entities .stream() .flatMap(e -> e.getRelations().stream()) .filter(e -> RelationType.ManyToMany.equals(e.getRelationType())) .collect(Collectors.toList()); } private boolean nonDefaultPrimaryKeyFields(final JdlField f) { return !isDefaultPrimaryKey(f); } private boolean isDefaultPrimaryKey(JdlField f) { return f.isPrimaryKey() && f.getType().equals(LONG) && f.getName().equals("id"); } protected Optional buildEntity(final Map.Entry> entry) { final List fields = entry .getValue() .stream() .map(this::buildField) .filter(Optional::isPresent) .map(Optional::get) .filter(this::nonDefaultPrimaryKeyFields) .collect(Collectors.toList()); final List existingRelations = new ArrayList<>(); final List relations = entry .getValue() .stream() .filter(SqlColumn::isForeignKey) .map((SqlColumn column) -> buildRelation(column, sqlService.getTableOfForeignKey(column), existingRelations)) .filter(Optional::isPresent) .map(Optional::get) .sorted() .collect(Collectors.toList()); String tableName = entry.getKey().getName(); String entityName = getEntityNameFormatted(tableName); List reserved = properties.getReservedList(); if (reserved.contains(entityName.toUpperCase())) { String msg = "Skipping processing table [" + entry.getKey().getName() + "] because " + " the transformed entity name [" + entityName + "] matches with one of the keywords " + reserved; log.error(msg); return Optional.empty(); } JdlEntity jdlEntity = new JdlEntityImpl( entityName, properties.getAddTableNameJdl() ? tableName : null, fields, entry.getKey().getComment().orElse(null), sqlService.isEnumTable(entry.getKey().getName()), !entry.getKey().isUpdatable(), sqlService.isPureManyToManyTable(entry.getKey().getName()), relations ); return Optional.of(jdlEntity); } private String getEntityNameFormatted(final String name) { return JdlUtils.getEntityName(name, properties.getDatabaseObjectPrefix()); } /** * @param column Column from which to create field * @return The field or empty if field is to be ignored */ protected Optional buildField(final SqlColumn column) { final String name; JdlFieldEnum jdlType; final String enumEntityName; final String comment; final boolean isNativeEnum = column.isNativeEnum(); String pattern = null; if (sqlService.isEnumTable(column.getTable().getName())) return Optional.empty(); if (column.isForeignKey()) { // check if table referenced is an enum, otherwise, skip final SqlTable tableOfForeignKey = sqlService.getTableOfForeignKey(column); if (!sqlService.isEnumTable(tableOfForeignKey.getName())) { log.info("Skipped field of ({}) as ({}) is not an enum table", column, tableOfForeignKey); return Optional.empty(); } jdlType = ENUM; name = SqlUtils.changeToCamelCase(SqlUtils.removeIdFromEnd(column.getName())); enumEntityName = StringUtils.capitalize(SqlUtils.changeToCamelCase(SqlUtils.removeIdFromEnd(tableOfForeignKey.getName()))); comment = column .getComment() .map(comment1 -> tableOfForeignKey.getComment().map(c -> comment1 + ". " + c).orElse(comment1)) .orElse(tableOfForeignKey.getComment().orElse(null)); } else { if (isNativeEnum) { jdlType = ENUM; name = SqlUtils.changeToCamelCase(toTitleCase(column.getName()).replace(" ", "")); // todo name of enumEntityName is not great but never mind enumEntityName = StringUtils.capitalize(SqlUtils.changeToCamelCase(SqlUtils.removeIdFromEnd(column.getName()))); } else { jdlType = sqlJdlTypeService.sqlTypeToJdlType(column.getType()); name = SqlUtils.changeToCamelCase(replaceSlavenChars(toTitleCase(column.getName()))); log.info("column name change sql to jdl format: {}, {}", column.getName(), name); enumEntityName = null; } if (jdlType == UNSUPPORTED) { comment = String.join(column.getComment().orElse(""), " ", column.getType()); } else { comment = column.getComment().orElse(null); } // comment = (jdlType=UNSUPPORTED) column.getComment().orElse(null); } final Integer min; final Integer max; switch (jdlType) { // We always define max for string case STRING -> { min = null; max = sqlJdlTypeService.calculateStringMaxLength(column); } case TIME_AS_TEXT -> { pattern = "^(([0-1]\\d)|(2[0-3])):([0-5]\\d):([0-5]\\d)$"; jdlType = STRING; max = 8; min = 8; } case YEAR_AS_TEXT -> { pattern = "^-?(\\d+)$"; jdlType = STRING; max = null; min = null; } case GEOMETRY_AS_TEXT, JSON_AS_TEXT, STRING_UNBOUNDED -> { jdlType = STRING; max = null; min = null; } default -> { min = null; max = null; } } // Min and pattern are not set as we cannot guess it (unless we define it in comments -> parse) return Optional.of( new JdlFieldImpl( jdlType, name, !column.isNullable(), comment, min, max, pattern, enumEntityName, isNativeEnum, column.isUnique(), column.isPrimaryKey() ) ); } /** * @param column Column from which to create relation, owner side of the relation * @param inverseSideTable The table referenced by the column of the owner side * @param existingRelations Cache for checking already registered relations, needed to avoid name duplication * @return The relation or empty if relation is to be ignored */ protected Optional buildRelation( final SqlColumn column, final SqlTable inverseSideTable, final List existingRelations ) { if (!column.isForeignKey()) throw new IllegalArgumentException("Cannot create a relation from a non foreign key"); final SqlTable ownerSideTable = column.getTable(); final String tableName = ownerSideTable.getName(); final String columnName = column.getName(); final boolean isNullable = column.isNullable(); final boolean isUnique = column.isUnique(); if (sqlService.isEnumTable(inverseSideTable.getName())) { log.info("Skipped relation of ({}) as ({}) is an enum table", column, inverseSideTable); return Optional.empty(); } final boolean isPureManyToManyTable = sqlService.isPureManyToManyTable(tableName); // it allows to have a clearer idea when reading the generated file final String extraRelationComment = column.getTable().getComment().orElse(null); final RelationType relationType; if (isPureManyToManyTable) { relationType = RelationType.ManyToMany; } else { if (isUnique) { relationType = RelationType.OneToOne; } else { relationType = RelationType.ManyToOne; } } // TeamMember{user(login) required} to User final String inverseSideEntityName = getEntityNameFormatted(inverseSideTable.getName()); // We put always bidirectional but we have no way to generate good inverse name so we put the owner side name final String ownerEntityName = getEntityNameFormatted(tableName); final String inverseSideRelationName = buildInverseSideRelationName( inverseSideEntityName, ownerEntityName, columnName, existingRelations ); boolean required = !isNullable; if (required) { if (ownerEntityName.equals(inverseSideEntityName)) { String msg = "Detected a Self Reference in the table " + tableName + ". JHipster JDL currently does not support Reflexive relationships. " + "Set [nullable] as [true] for column [" + columnName + "] to fix errors when using the JDL with JHipster"; log.warn(msg); required = false; } } final JdlRelationImpl relation = new JdlRelationImpl( relationType, properties.isAssumeBidirectional(), required, false, ownerEntityName, inverseSideEntityName, SqlUtils.changeToCamelCase(SqlUtils.removeIdFromEnd(columnName)), sqlService.getDisplayFieldOfTable(inverseSideTable), column.getComment().orElse(null), null, inverseSideRelationName, sqlService.getDisplayFieldOfTable(ownerSideTable), extraRelationComment ); existingRelations.add(relation); return Optional.of(relation); } /** * @param inverseSideEntityName The table name referenced by the column of the owner side * @param ownerEntityName Table name of column from which to create relation, owner side of the relation * @param columnName Column name from which to create relation, owner side of the relation * @param existingRelations Cache for checking already registered relations, needed to avoid name duplication * @return Modified name of ownerEntity or full name consisting of ownerEntity and columnName */ private String buildInverseSideRelationName( String inverseSideEntityName, String ownerEntityName, String columnName, List existingRelations ) { String possibleRelationName = JdlUtils.decapitalize(ownerEntityName); // Looking for a full match of names within 1 entity final boolean relationAlreadyExist = existingRelations .stream() .anyMatch(jdlRelation -> ownerEntityName.equals(jdlRelation.getOwnerEntityName()) && possibleRelationName.equals(jdlRelation.getInverseSideRelationName().orElse(null)) && inverseSideEntityName.equals(jdlRelation.getInverseSideEntityName()) ); if (relationAlreadyExist) { //As stated earlier, we have no way to generate a good reverse name, so we put the owner's side name. //So we simply combine the relationship name and the column name return possibleRelationName + "Of" + StringUtils.capitalize(SqlUtils.changeToCamelCase(SqlUtils.removeIdFromEnd(columnName))); } else { return possibleRelationName; } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/logic/MustacheService.java ================================================ package org.blackdread.sqltojava.service.logic; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; import com.github.mustachejava.reflect.ReflectionObjectHandler; import com.github.mustachejava.util.DecoratedCollection; import java.io.IOException; import java.io.StringWriter; import java.util.Collection; import java.util.Map; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.entity.impl.JdlEntityImpl; import org.blackdread.sqltojava.entity.impl.JdlFieldImpl; import org.blackdread.sqltojava.entity.impl.JdlRelationGroupImpl; import org.blackdread.sqltojava.entity.impl.JdlRelationImpl; import org.blackdread.sqltojava.view.mapper.JdlViewMapperImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class MustacheService { private static final Logger log = LoggerFactory.getLogger(MustacheService.class); private static final String MUSTACHE_EXTENSION = ".mustache"; private final MustacheFactory mf; private final ApplicationProperties properties; public MustacheService(ApplicationProperties properties) { this.mf = getMustacheFactory(); this.properties = properties; } /** * Load templates from resources/mustache. The assumed extension is .mustache so only the file name needs to be specified. * @param template * @param context * @return */ public String executeTemplate(String template, Map context) { Mustache m = mf.compile(template + MUSTACHE_EXTENSION); try { StringWriter writer = new StringWriter(); m.execute(writer, context).flush(); return replaceMultpleBlankLinesWithOne(writer.toString()); } catch (IOException e) { throw new RuntimeException(e); } } private String replaceMultpleBlankLinesWithOne(String s) { return s.replaceAll("([ \\tw]*\\n){3,}", "\\\n\\\n").replaceAll("\\n\\n$", "\\\n"); } /** * Create a MustacheFactory with a MapstructBasedObjectHandler * @return MustacheFactory */ private MustacheFactory getMustacheFactory() { DefaultMustacheFactory mf = new DefaultMustacheFactory("mustache"); mf.setObjectHandler(new MapstructBasedObjectHandler()); return mf; } /** * Use Mapstruct to map the JDL model to view DTOs for rendering with mustache.java */ private class MapstructBasedObjectHandler extends ReflectionObjectHandler { private static JdlViewMapperImpl mapper = new JdlViewMapperImpl(); @Override public Object coerce(Object o) { if (o instanceof JdlRelationGroupImpl) return mapper.relationToView((JdlRelationGroupImpl) o); if (o instanceof JdlEntityImpl) return mapper.entityToView((JdlEntityImpl) o, properties.getUndefinedTypeHandling()); if (o instanceof JdlFieldImpl) return mapper.fieldToView((JdlFieldImpl) o); if (o instanceof JdlRelationImpl) return mapper.relationToView((JdlRelationImpl) o); if (o instanceof Collection) return new DecoratedCollection((Collection) o); return super.coerce(o); } } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/service/logic/SqlService.java ================================================ package org.blackdread.sqltojava.service.logic; import com.google.common.collect.Maps; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.config.DatabaseObjectTypesConfigEnum; import org.blackdread.sqltojava.entity.SqlColumn; import org.blackdread.sqltojava.entity.SqlTable; import org.blackdread.sqltojava.entity.impl.SqlColumnImpl; import org.blackdread.sqltojava.entity.impl.SqlTableImpl; import org.blackdread.sqltojava.pojo.ColumnInformation; import org.blackdread.sqltojava.pojo.TableInformation; import org.blackdread.sqltojava.pojo.TableRelationInformation; import org.blackdread.sqltojava.service.InformationSchemaService; import org.blackdread.sqltojava.util.SqlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Transactional(readOnly = true) public class SqlService { private static final Logger log = LoggerFactory.getLogger(SqlService.class); private final ApplicationProperties applicationProperties; private final InformationSchemaService informationSchemaService; @Autowired public SqlService(final ApplicationProperties applicationProperties, final InformationSchemaService informationSchemaService) { this.applicationProperties = applicationProperties; this.informationSchemaService = informationSchemaService; } // @Cacheable does not work when in same class so we loose a bit in efficiency but repo is cached as well so... @Cacheable("SqlService.buildTables") public List buildTables() { log.debug("buildTables called"); final List ignoredTableNames = applicationProperties.getIgnoredTableNames(); return informationSchemaService .getAllTableInformation() .stream() .filter(table -> includeType(table, applicationProperties.getDatabaseObjectTypesConfig())) .filter(table -> !doesTableEndWithDetailKeyword(table)) .filter(table -> !isTableIgnored(ignoredTableNames, table)) .map(table -> new SqlTableImpl(table.getName(), table.getComment().orElse(null), table.getType(), table.isUpdateable())) .collect(Collectors.toList()); } private boolean includeType(TableInformation table, DatabaseObjectTypesConfigEnum databaseObjectTypesConfig) { return switch (databaseObjectTypesConfig) { case ALL -> true; case TABLES -> table.getType().equals("BASE TABLE"); case VIEWS -> table.getType().equals("VIEW"); }; } private boolean doesTableEndWithDetailKeyword(TableInformation table) { String tablename = table.getName(); boolean result = tablename.toLowerCase().endsWith("_detail"); if (result) { String msg = String.format( "Skipped processing table [%s] which ends with the JDL keyword [Detail]. " + "Please alter the table name to something else (for e.g., adding [details] as the suffix)", tablename ); log.error(msg); } return result; } @Cacheable("SqlService.buildColumns") public List buildColumns() { log.debug("buildColumns called"); return buildTables() .stream() .map(table -> Maps.immutableEntry(table, informationSchemaService.getFullColumnInformationOfTable(table.getName()))) .map(entry -> entry .getValue() .stream() .map(columnInformation -> { final String columnType = columnInformation.getType(); // hard coded for now, we can later extract in some service, etc. final boolean isNativeEnum = columnType.startsWith("enum") || columnType.startsWith("set"); return new SqlColumnImpl( entry.getKey(), columnInformation.getName(), columnType, columnInformation.isPrimary(), isForeignKey(entry.getKey().getName(), columnInformation.getName()), columnInformation.isNullable(), columnInformation.isUnique(), isNativeEnum, columnInformation.getDefaultValue().orElse(null), columnInformation.getComment() ); }) .collect(Collectors.toList()) ) .flatMap(Collection::stream) .collect(Collectors.toList()); } @Cacheable("SqlService.getTableOfForeignKey") public SqlTable getTableOfForeignKey(final SqlColumn column) { log.debug("getTableOfForeignKey called: ({}) ({})", column.getTable().getName(), column.getName()); return informationSchemaService .getAllTableRelationInformation() .stream() .filter(e -> e.getTableName().equals(column.getTable().getName())) .filter(e -> e.getColumnName().equals(column.getName())) .findFirst() .map(TableRelationInformation::getReferencedTableName) .map(tableName -> buildTables().stream().filter(e -> e.getName().equals(tableName)).findFirst()) .map(sqlTable -> sqlTable.orElseThrow(() -> new IllegalStateException("Table not found or is ignored"))) .orElseThrow(() -> new IllegalArgumentException("Column is not a foreign key")); } /** * Get the display field for a table. * The display field to chosen as a column with a unique constraint the with a unique constraint that has the lowest original position. * @param table * @return */ @Cacheable("SqlService.getDisplayFieldOfTable") public String getDisplayFieldOfTable(final SqlTable table) { String tableName = table.getName(); log.debug("getDisplayFieldOfTable called: ({})", tableName); return informationSchemaService .getFullColumnInformationOfTable(table.getName()) .stream() .sorted(Comparator.comparingInt(ColumnInformation::getOrdinalPosition)) .filter(ColumnInformation::isUnique) .filter(c -> !isForeignKey(tableName, c.getName())) .findFirst() .map(ColumnInformation::getName) .orElse(null); } @Cacheable("SqlService.isEnumTable") public boolean isEnumTable(final String tableName) { final List table = informationSchemaService.getFullColumnInformationOfTable(tableName); if (table.size() != 2) return false; for (final ColumnInformation column : table) { // Our design contract define that id and code/name is an enum table so logic is put here if ( !column.getName().equalsIgnoreCase("id") && (!column.getName().equalsIgnoreCase("code") && !column.getName().equalsIgnoreCase("name")) ) return false; } return true; } /** * Used to get enums values for an enum materialized with a table * * @param tableName Table name * @return enum values * @deprecated not implemented yet */ @Cacheable("SqlService.getEnumValuesForTable") public List getEnumValues(final String tableName) { throw new IllegalStateException("todo"); } /** * Used to get enums values for a native enum * * @param tableName Table name * @param columnName column name * @return enum values * @deprecated not implemented yet */ @Cacheable("SqlService.getEnumValuesForColumn") public List getEnumValues(final String tableName, final String columnName) { throw new IllegalStateException("todo"); } /** * Check if the column name passed from the table name specified is referencing an enum table * * @param tableName Table containing the FK column * @param columnName FK column that reference the enum table * @return True if this column name is referencing an enum table */ @Cacheable("SqlService.isForeignKeyFromAnEnumTable") public boolean isForeignKeyFromAnEnumTable(final String tableName, final String columnName) { if (!isForeignKey(tableName, columnName)) return false; return isEnumTable(SqlUtils.removeIdFromEnd(columnName)); } /** * @param tableName Table to check * @param columnName Column to check * @return True if column is a foreign key */ @Cacheable("SqlService.isForeignKey") public boolean isForeignKey(final String tableName, final String columnName) { return informationSchemaService .getAllTableRelationInformation() .stream() .filter(e -> e.getTableName().equals(tableName)) .anyMatch(e -> e.getColumnName().equals(columnName)); } /** * A pure many to many table is a table with 2 columns and both are foreign keys. *

Other MtM tables add other columns and treated as real entities with MtO relations

* * @param tableName Table to check * @return True if this table is a pure many to many table (2 columns, 2 FK) */ @Cacheable("SqlService.isPureManyToManyTable") public boolean isPureManyToManyTable(final String tableName) { final List table = informationSchemaService.getFullColumnInformationOfTable(tableName); if (table.size() != 2) return false; for (final ColumnInformation column : table) { if (!isForeignKey(tableName, column.getName())) return false; } return true; } private boolean isTableIgnored(final List ignoredTableNames, final TableInformation table) { return ignoredTableNames.contains(table.getName()); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/util/AppUtil.java ================================================ package org.blackdread.sqltojava.util; import org.blackdread.sqltojava.listener.SetDatabaseProfileApplicationEventListener; import org.springframework.boot.SpringApplication; public final class AppUtil { /** * Utility method to configure application the same in both tests and production since the * main method is not called when the tests run and the database profile needs to be set in * ApplicationEnvironmentPreparedEvent which runs before the context is loaded. * @param app * @return */ public static SpringApplication setup(SpringApplication app) { app.addListeners(new SetDatabaseProfileApplicationEventListener()); return app; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/util/JdbcUtil.java ================================================ package org.blackdread.sqltojava.util; public final class JdbcUtil { /** * This method assumes the jdbc url starts with "jdbc:database_type:" * @param jdbcUrl * @return */ public static String getDatabaseTypeFromJdbcUrl(String jdbcUrl) { String[] parts = jdbcUrl.split(":"); assert parts[0].equals("jdbc"); return parts[1]; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/util/JdlUtils.java ================================================ package org.blackdread.sqltojava.util; import java.util.ArrayList; import java.util.List; import javax.validation.constraints.NotNull; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class JdlUtils { private static final Logger log = LoggerFactory.getLogger(JdlUtils.class); private JdlUtils() {} @NotNull public static String paginationAll() { return "paginate * with pagination"; } @NotNull public static String serviceClassAll() { return "service * with serviceClass"; } @NotNull public static String mapStructAll() { return "dto * with mapstruct"; } @NotNull public static String angularSuffixAll(final String suffix) { return "angularSuffix * with " + suffix; } @NotNull public static String filterAll() { return "filter *"; } public static String getEntityName(String name, List prefixes) { String entityName = prefixes .stream() .filter(prefix -> name.startsWith(prefix)) .findFirst() .map(s -> name.substring(s.length())) .orElse(name); return StringUtils.capitalize(SqlUtils.changeToCamelCase(entityName)); } public static String decapitalize(String string) { char c[] = string.toCharArray(); c[0] = Character.toLowerCase(c[0]); return new String(c); } public static List getOptions() { List options = new ArrayList<>(); options.add(JdlUtils.serviceClassAll()); options.add(JdlUtils.paginationAll()); options.add(JdlUtils.mapStructAll()); options.add(JdlUtils.filterAll()); return options; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/util/NamingConventionUtil.java ================================================ package org.blackdread.sqltojava.util; import java.util.Arrays; import java.util.stream.Collectors; public final class NamingConventionUtil { public static String replaceSlavenChars(String columnName) { return columnName .replace(" ", "") .replace("š", "s") .replace("č", "c") .replace("ž", "z") .replace("ć", "c") .replace("đ", "d") .replace(" ", "") .replace("Š", "S") .replace("Č", "C") .replace("Ž", "Z") .replace("Ć", "C") .replace("Đ", "D"); } public static String toTitleCase(String sentence) { if (sentence == null || sentence.isEmpty()) { return sentence; } if (!sentence.contains(" ")) { return sentence; } return Arrays .stream(sentence.split(" ")) .map(word -> Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase()) .collect(Collectors.joining(" ")); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/util/ResourceUtil.java ================================================ package org.blackdread.sqltojava.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; public final class ResourceUtil { private static final Logger log = LoggerFactory.getLogger(ResourceUtil.class); public static String readString(Resource resource) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) { return reader.lines().collect(Collectors.joining(System.lineSeparator())); } catch (IOException e) { throw new RuntimeException(e); } } public static String readString(String path) { try { InputStream is = ResourceUtil.class.getClassLoader().getResourceAsStream(path); String text = new String(is.readAllBytes(), StandardCharsets.UTF_8); return text; } catch (IOException e) { throw new RuntimeException(e); } } public static String getFirstExistingResourcePath(String fileName, String... paths) { for (String path : paths) { if (ResourceUtil.class.getResource(path) != null) { log.info(String.format("%s found at %s", fileName, path)); return path; } else { log.info(String.format("%s not found at %s", fileName, path)); } } throw new RuntimeException(String.format("Could not find %s", fileName)); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/util/SqlUtils.java ================================================ package org.blackdread.sqltojava.util; import static com.google.common.base.CaseFormat.LOWER_CAMEL; import static java.util.Optional.empty; import com.google.common.base.CaseFormat; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.blackdread.sqltojava.entity.SqlColumn; import org.blackdread.sqltojava.entity.SqlTable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class SqlUtils { private static final Logger log = LoggerFactory.getLogger(SqlUtils.class); public static final Pattern COLUMN_TYPE_SIZE_REGEX = Pattern.compile("(^[a-z0-9\\s]+)(\\((\\-?[0-9]+)\\))?$", Pattern.CASE_INSENSITIVE); private SqlUtils() {} /** * @param value A string that might ends with "_id" or "Id" * @return Value without any ID suffix */ public static String removeIdFromEnd(final String value) { return value.toLowerCase().endsWith("_id") ? value.substring(0, value.length() - 3) : value.endsWith("Id") ? value.substring(0, value.length() - 2) : value; } public static String changeToCamelCase(final String value) { if (value.contains("_")) { return CaseFormat.LOWER_UNDERSCORE.to(LOWER_CAMEL, value); } else if (value.equals(value.toUpperCase())) { // If the string is in all uppercase, convert it to lowercase return value.toLowerCase(); } else { // If the string contains uppercase letters in the middle, split the string into words // based on the uppercase letters, then join the words with the first word in lowercase // and the rest of the words in their original case String[] words = value.split("(?=[A-Z])"); String res = Stream.of(words).map(word -> word.equals(words[0]) ? word.toLowerCase() : word).collect(Collectors.joining()); return changeIdToLowerCase(res).replace("ID", "Id"); } } public static String changeIdToLowerCase(final String value) { if (value == null || value.isEmpty()) { return value; } if (value.startsWith("iD") && Character.isUpperCase(value.charAt(2))) { return "id" + value.substring(2); } return value; } public static Map> groupColumnsByTable(final List sqlColumns) { return sqlColumns.stream().collect(Collectors.groupingBy(SqlColumn::getTable, Collectors.toList())); } public static Optional parseSqlSize(String value) { value = StringUtils.trim(value); final Matcher matcher = COLUMN_TYPE_SIZE_REGEX.matcher(value); if ("character varying".equalsIgnoreCase(value)) { // quick fix due to new regex used -> "matcher.matches()" returns true return empty(); } if (matcher.matches()) { return Optional.of(Integer.valueOf(matcher.group(3))); } log.warn("Did not find sql size from: {}", value); return empty(); } public static String parseSqlType(String value) { // todo: Oracle Bigint, BigDecimal if (value.equals("NUMBER(38)") || value.equals("NUMBER(19,5)")) { return value; } return value.split("\\(")[0]; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/JdlCommentView.java ================================================ package org.blackdread.sqltojava.view; import java.util.Optional; /** * Methods for comments in the view */ public interface JdlCommentView { Optional getComment(); default String getImplComment(String comment) { return (comment != null) ? "// " + comment : null; } default String getDocComment(String comment) { return (comment != null) ? "/** " + comment + " */" : null; } default String getDocComment() { return getDocComment(getComment().orElse(null)); } default String getImplComment() { return getImplComment(getComment().orElse(null)); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/JdlEntityView.java ================================================ package org.blackdread.sqltojava.view; import static org.slf4j.LoggerFactory.getLogger; import java.util.List; import java.util.stream.Collectors; import org.blackdread.sqltojava.config.UndefinedJdlTypeHandlingEnum; import org.blackdread.sqltojava.entity.JdlEntity; import org.blackdread.sqltojava.entity.JdlField; import org.slf4j.Logger; /** * Methods for JDL entities in the view */ public interface JdlEntityView extends JdlEntity, JdlCommentView { UndefinedJdlTypeHandlingEnum getUndefinedJdlTypeHandling(); default String getType() { return (isEnumEntity()) ? "enum" : "entity"; } default String tableName() { return (getTableName() != null) ? String.format("(%s)", getTableName()) : null; } default List filteredFields() { return getFields().stream().filter(f -> filterUnsupported(getUndefinedJdlTypeHandling(), f)).collect(Collectors.toList()); } /** * Filters fields and throws error based on UndefinedJdlTypeHandling * * @return */ default boolean filterUnsupported(UndefinedJdlTypeHandlingEnum undefinedJdlTypeHandling, JdlField field) { switch (field.getType()) { case UNSUPPORTED -> { switch (undefinedJdlTypeHandling) { case UNSUPPORTED -> { return true; } case SKIP -> { log().warn("Skipping unsupportd field {}", field); return false; } case ERROR -> throw new RuntimeException(String.format("Unsupported jdl type %s", field)); // case ERROR -> { // log().error("Unsupported jdl type {}", field); // return false; // } default -> throw new RuntimeException( String.format("Unhandled UndefinedJdlTypeHandlingEnum: %s", undefinedJdlTypeHandling) ); } } default -> { return true; } } } private static Logger log() { final class LogHolder { private static final Logger LOGGER = getLogger(JdlEntityView.class); } return LogHolder.LOGGER; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/JdlFieldView.java ================================================ package org.blackdread.sqltojava.view; import com.google.common.base.Joiner; import java.util.ArrayList; import java.util.List; import javax.validation.constraints.NotNull; import org.blackdread.sqltojava.entity.JdlField; import org.blackdread.sqltojava.entity.JdlFieldEnum; /** * Methods for JDL fields in the view */ public interface JdlFieldView extends JdlField, JdlCommentView { //TODO This validation should be handled in JdlService default String getTypeName() { JdlFieldEnum type = getType(); return switch (type) { case ENUM -> getEnumEntityName() .orElseThrow(() -> new IllegalStateException("An enum field must have its enum entity name set")); default -> (("UUID".equals(type.name())) ? type.name() : type.toCamelUpper()); }; } default String constraints() { List constraints = new ArrayList<>(); if (renderRequired()) constraints.add(requiredConstraint()); if (isUnique()) constraints.add(uniqueConstraint()); getMin().ifPresent(min -> constraints.add(minConstraint(min))); getMax().ifPresent(max -> constraints.add(maxConstraint(max))); getPattern().ifPresent(pattern -> constraints.add(patternConstraint(pattern))); return (!constraints.isEmpty()) ? " ".concat(Joiner.on(" ").join(constraints)) : null; } // TODO do not write required when field is primary key default boolean renderRequired() { // if (!isPrimaryKey() && isRequired() && ) constraints.add(JdlUtils.validationRequired()); return isRequired() && !(getName().equals("id") && getType().equals(JdlFieldEnum.UUID)); } @NotNull default String requiredConstraint() { return "required"; } @NotNull default String uniqueConstraint() { return "unique"; } default String minConstraint(int min) { return switch (getType()) { case STRING -> validationMinLength(min); default -> validationMin(min); }; } default String maxConstraint(int max) { return switch (getType()) { case STRING -> validationMaxLength(max); default -> validationMax(max); }; } //TODO This validation should be handled in JdlService default String patternConstraint(String pattern) { return switch (getType()) { case STRING -> validationPattern(pattern); default -> throw new RuntimeException("Only String can have a pattern"); }; } @NotNull default String validationMax(final int max) { return "max(" + max + ")"; } @NotNull default String validationMin(final int min) { return "min(" + min + ")"; } default String validationMaxLength(final int max) { return "maxlength(" + max + ")"; } @NotNull default String validationMinLength(final int min) { return "minlength(" + min + ")"; } @NotNull default String validationPattern(final String pattern) { return "pattern(/" + pattern + "/)"; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/JdlRelationGroupView.java ================================================ package org.blackdread.sqltojava.view; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; @Immutable @ThreadSafe public interface JdlRelationGroupView {} ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/JdlRelationView.java ================================================ package org.blackdread.sqltojava.view; import java.util.Optional; import org.blackdread.sqltojava.entity.JdlRelation; import org.blackdread.sqltojava.entity.RelationType; /** * Methods for JDL relations in the view */ public interface JdlRelationView extends JdlRelation, JdlCommentView { default String getType() { return getRelationType().toJdl(); } default boolean isPureManyToMany() { return getRelationType() == RelationType.ManyToMany; } default String getOwnerDocComment() { return getDocComment(getOwnerComment().orElse(null)); } default boolean hasComments() { return getOwnerComment().isPresent() || getInverseSideComment().isPresent(); } default String getInverseDocComment() { return getDocComment(getInverseSideComment().orElse(null)); } default String getOwnerConfig() { return "{" + getOwnerRelationName() + getRelationConfig(getOwnerDisplayField(), isOwnerRequired()) + "}"; } default String getInverseConfig() { if (isBidirectional()) { return ( "{" + getInverseSideRelationName().orElse("") + getRelationConfig(getInverseSideDisplayField(), isInverseSideRequired()) + "}" ); } else { return null; } } default String getDisplayField(Optional relation) { return relation.map(displayName -> "(" + displayName + ")").orElse(""); } default String getRelationConfig(Optional relation, boolean required) { return getDisplayField(relation) + ((required) ? " required" : ""); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/impl/JdlEntityViewImpl.java ================================================ package org.blackdread.sqltojava.view.impl; import java.util.List; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import org.blackdread.sqltojava.config.UndefinedJdlTypeHandlingEnum; import org.blackdread.sqltojava.entity.JdlField; import org.blackdread.sqltojava.entity.JdlRelation; import org.blackdread.sqltojava.entity.impl.JdlEntityImpl; import org.blackdread.sqltojava.view.JdlEntityView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Immutable @ThreadSafe public class JdlEntityViewImpl extends JdlEntityImpl implements JdlEntityView { private static final Logger log = LoggerFactory.getLogger(JdlEntityViewImpl.class); private final UndefinedJdlTypeHandlingEnum undefinedJdlTypeHandling; /** * Constructory based on super * @param name * @param tableName * @param fields * @param comment * @param enumEntity * @param readOnly * @param pureManyToMany * @param relations */ public JdlEntityViewImpl( String name, String tableName, List fields, @Nullable String comment, boolean enumEntity, boolean readOnly, boolean pureManyToMany, List relations, UndefinedJdlTypeHandlingEnum undefinedJdlTypeHandling ) { super(name, tableName, fields, comment, enumEntity, readOnly, pureManyToMany, relations); this.undefinedJdlTypeHandling = undefinedJdlTypeHandling; } @Override public UndefinedJdlTypeHandlingEnum getUndefinedJdlTypeHandling() { return undefinedJdlTypeHandling; } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/impl/JdlFieldViewImpl.java ================================================ package org.blackdread.sqltojava.view.impl; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.blackdread.sqltojava.entity.impl.JdlFieldImpl; import org.blackdread.sqltojava.service.logic.JdlService; import org.blackdread.sqltojava.view.JdlFieldView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Immutable @ThreadSafe public class JdlFieldViewImpl extends JdlFieldImpl implements JdlFieldView { private static final Logger log = LoggerFactory.getLogger(JdlService.class); /** * Constructory based on super * @param type * @param name * @param required * @param comment * @param min * @param max * @param pattern * @param enumEntityName * @param nativeEnum * @param unique * @param primaryKey */ public JdlFieldViewImpl( JdlFieldEnum type, String name, boolean required, @Nullable String comment, @Nullable Integer min, @Nullable Integer max, @Nullable String pattern, @Nullable String enumEntityName, Boolean nativeEnum, Boolean unique, Boolean primaryKey ) { super(type, name, required, comment, min, max, pattern, enumEntityName, nativeEnum, unique, primaryKey); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/impl/JdlRelationGroupViewImpl.java ================================================ package org.blackdread.sqltojava.view.impl; import java.util.List; import java.util.Optional; import org.blackdread.sqltojava.entity.JdlRelation; import org.blackdread.sqltojava.entity.RelationType; import org.blackdread.sqltojava.entity.impl.JdlRelationGroupImpl; public class JdlRelationGroupViewImpl extends JdlRelationGroupImpl { public JdlRelationGroupViewImpl(RelationType relationType, List relations) { super(relationType, relations); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/impl/JdlRelationViewImpl.java ================================================ package org.blackdread.sqltojava.view.impl; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import org.blackdread.sqltojava.entity.RelationType; import org.blackdread.sqltojava.entity.impl.JdlRelationImpl; import org.blackdread.sqltojava.view.JdlRelationView; @Immutable @ThreadSafe public class JdlRelationViewImpl extends JdlRelationImpl implements JdlRelationView { /** * Constructory based on super * @param relationType * @param bidirectional * @param ownerRequired * @param inverseSideRequired * @param ownerEntityName * @param inverseSideEntityName * @param ownerRelationName * @param ownerDisplayField * @param ownerComment * @param inverseSideComment * @param inverseSideRelationName * @param inverseSideDisplayField * @param comment */ public JdlRelationViewImpl( RelationType relationType, boolean bidirectional, boolean ownerRequired, boolean inverseSideRequired, String ownerEntityName, String inverseSideEntityName, String ownerRelationName, @Nullable String ownerDisplayField, @Nullable String ownerComment, @Nullable String inverseSideComment, @Nullable String inverseSideRelationName, @Nullable String inverseSideDisplayField, @Nullable String comment ) { super( relationType, bidirectional, ownerRequired, inverseSideRequired, ownerEntityName, inverseSideEntityName, ownerRelationName, ownerDisplayField, ownerComment, inverseSideComment, inverseSideRelationName, inverseSideDisplayField, comment ); } } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/mapper/JdlViewMapper.java ================================================ package org.blackdread.sqltojava.view.mapper; import org.blackdread.sqltojava.config.UndefinedJdlTypeHandlingEnum; import org.blackdread.sqltojava.entity.impl.JdlEntityImpl; import org.blackdread.sqltojava.entity.impl.JdlFieldImpl; import org.blackdread.sqltojava.entity.impl.JdlRelationGroupImpl; import org.blackdread.sqltojava.entity.impl.JdlRelationImpl; import org.blackdread.sqltojava.view.impl.JdlEntityViewImpl; import org.blackdread.sqltojava.view.impl.JdlFieldViewImpl; import org.blackdread.sqltojava.view.impl.JdlRelationGroupViewImpl; import org.blackdread.sqltojava.view.impl.JdlRelationViewImpl; import org.mapstruct.Mapper; /** * Generate Mapstruct mapper sources on compile */ @Mapper(componentModel = "spring", uses = OptionalUtils.class) public interface JdlViewMapper { JdlEntityViewImpl entityToView(JdlEntityImpl jdlEntity, UndefinedJdlTypeHandlingEnum undefinedJdlTypeHandling); JdlFieldViewImpl fieldToView(JdlFieldImpl jdlField); JdlRelationViewImpl relationToView(JdlRelationImpl jdlRelation); JdlRelationGroupViewImpl relationToView(JdlRelationGroupImpl jdlRelation); } ================================================ FILE: src/main/java/org/blackdread/sqltojava/view/mapper/OptionalUtils.java ================================================ package org.blackdread.sqltojava.view.mapper; import java.util.Optional; /** * Used to handle Optional with Mapstruct */ public class OptionalUtils { private OptionalUtils() {} public static T fromOptional(Optional optional) { return optional.orElse(null); } } ================================================ FILE: src/main/resources/application.yml ================================================ spring: datasource: type: com.zaxxer.hikari.HikariDataSource url: jdbc:mysql://localhost:3306/?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC username: root password: hikari: data-source-properties: cachePrepStmts: true prepStmtCacheSize: 250 prepStmtCacheSqlLimit: 2048 useServerPrepStmts: true flyway: enabled: false application: reserved-keywords: classpath:reserved/keywords.json database-to-export: dbo database_object_types_config: ALL # TABLES, ViEWS, ALL render_entities_only: false assume_bidirectional: false database-object-prefix: - t_ - v_ add_table_name_jdl: true undefined_type_handling: ERROR jdl-type-overrides: my_type: FLOAT # Example JDL type override ignored-table-names: - databasechangelog - databasechangeloglock - QRTZ_BLOB_TRIGGERS - QRTZ_CALENDARS - QRTZ_CRON_TRIGGERS - QRTZ_FIRED_TRIGGERS - QRTZ_JOB_DETAILS - QRTZ_LOCKS - QRTZ_PAUSED_TRIGGER_GRPS - QRTZ_SCHEDULER_STATE - QRTZ_SIMPLE_TRIGGERS - QRTZ_SIMPROP_TRIGGERS - QRTZ_TRIGGERS - MSreplication_options - spt_fallback_db - spt_fallback_dev - spt_fallback_usg - spt_monitor - spt_values export: path: ./my-project-jdl.jh type: jdl export-file-structure-type: GROUPED_RELATIONS_SEPARATE_VIEWS export-mustache-template-filename-optional: ================================================ FILE: src/main/resources/mustache/application-entities-relations-views.mustache ================================================ {{> entities}} {{> onetoonerelations}} {{> manytoonerelations}} {{> manytomanyrelations}} {{> views}} {{> viewsrelations}} {{> options}} ================================================ FILE: src/main/resources/mustache/application-grouped-relations-separate-views.mustache ================================================ {{> entities}} {{> groupedonetoonerelations}} {{> groupedmanytoonerelations}} {{> manytomanyrelations}} {{> views}} {{> viewsrelations}} {{> options}} ================================================ FILE: src/main/resources/mustache/application.mustache ================================================ {{> entities}} {{> relations}} {{> options}} ================================================ FILE: src/main/resources/mustache/entities.mustache ================================================ {{#entities}} {{^value.isPureManyToMany}} {{#value.comment}} {{value.docComment}} {{/value.comment}} {{#value.isReadOnly}} @readOnly {{/value.isReadOnly}} {{value.type}} {{value.name}}{{value.tableName}} { {{#value.filteredFields}} {{#value.comment}} {{value.docComment}} {{/value.comment}} {{value.name}} {{value.typeName}}{{value.constraints}}{{^last}},{{/last}} {{/value.filteredFields}} } {{^last}}{{/last}} {{/value.isPureManyToMany}} {{/entities}} {{^entities}} // No entities were found for which JDL is to be generated. Please review console logs {{/entities}} ================================================ FILE: src/main/resources/mustache/groupedmanytoonerelations.mustache ================================================ {{#groupedmanytoonerelations}} {{#first}}// Grouped Relations{{/first}} {{#isPureManyToMany}} // TODO This is a pure ManyToMany relation (delete me and decide owner side) {{/isPureManyToMany}} {{#comment}} {{implComment}} {{/comment}} relationship {{relationType}} { {{#relations}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/relations}} } {{/groupedmanytoonerelations}} ================================================ FILE: src/main/resources/mustache/groupedonetoonerelations.mustache ================================================ {{#groupedonetoonerelations}} {{#first}}// Grouped Relations{{/first}} {{#isPureManyToMany}} // TODO This is a pure ManyToMany relation (delete me and decide owner side) {{/isPureManyToMany}} {{#comment}} {{implComment}} {{/comment}} relationship {{relationType}} { {{#relations}} {{^value.hasComments}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} {{#value.hasComments}} {{#value.ownerDocComment}} {{value.ownerDocComment}} {{/value.ownerDocComment}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{#value.inverseDocComment}} {{value.inverseDocComment}} {{/value.inverseDocComment}} {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} {{/relations}} } {{/groupedonetoonerelations}} ================================================ FILE: src/main/resources/mustache/manytomanyrelations.mustache ================================================ {{#manytomanyrelations}} {{#first}}// many-to-many Relations{{/first}} {{#value.isPureManyToMany}} // TODO This is a pure ManyToMany relation (delete me and decide owner side) {{/value.isPureManyToMany}} {{#value.comment}} {{value.implComment}} {{/value.comment}} relationship {{value.type}} { {{^value.hasComments}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} {{#value.hasComments}} {{#value.ownerDocComment}} {{value.ownerDocComment}} {{/value.ownerDocComment}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{#value.inverseDocComment}} {{value.inverseDocComment}} {{/value.inverseDocComment}} {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} } {{^last}}{{/last}} {{/manytomanyrelations}} ================================================ FILE: src/main/resources/mustache/manytoonerelations.mustache ================================================ {{#manytoonerelations}} {{#first}}//many-to-one Relations{{/first}} {{#value.isPureManyToMany}} // TODO This is a pure ManyToMany relation (delete me and decide owner side) {{/value.isPureManyToMany}} {{#value.comment}} {{value.implComment}} {{/value.comment}} relationship {{value.type}} { {{^value.hasComments}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} {{#value.hasComments}} {{#value.ownerDocComment}} {{value.ownerDocComment}} {{/value.ownerDocComment}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{#value.inverseDocComment}} {{value.inverseDocComment}} {{/value.inverseDocComment}} {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} } {{^last}}{{/last}} {{/manytoonerelations}} ================================================ FILE: src/main/resources/mustache/onetoonerelations.mustache ================================================ {{#onetoonerelations}} {{#first}}//one-to-one Relations{{/first}} {{#value.isPureManyToMany}} // TODO This is a pure ManyToMany relation (delete me and decide owner side) {{/value.isPureManyToMany}} {{#value.comment}} {{value.implComment}} {{/value.comment}} relationship {{value.type}} { {{^value.hasComments}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} {{#value.hasComments}} {{#value.ownerDocComment}} {{value.ownerDocComment}} {{/value.ownerDocComment}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{#value.inverseDocComment}} {{value.inverseDocComment}} {{/value.inverseDocComment}} {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} } {{^last}}{{/last}} {{/onetoonerelations}} ================================================ FILE: src/main/resources/mustache/options.mustache ================================================ {{#options}} {{#first}}// Options{{/first}} {{value}} {{/options}} ================================================ FILE: src/main/resources/mustache/relations.mustache ================================================ {{#relations}} {{#first}}// Relations{{/first}} {{#value.isPureManyToMany}} // TODO This is a pure ManyToMany relation (delete me and decide owner side) {{/value.isPureManyToMany}} {{#value.comment}} {{value.implComment}} {{/value.comment}} relationship {{value.type}} { {{^value.hasComments}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} {{#value.hasComments}} {{#value.ownerDocComment}} {{value.ownerDocComment}} {{/value.ownerDocComment}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{#value.inverseDocComment}} {{value.inverseDocComment}} {{/value.inverseDocComment}} {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} } {{^last}}{{/last}} {{/relations}} ================================================ FILE: src/main/resources/mustache/views.mustache ================================================ {{#views}} {{^value.isPureManyToMany}} {{#value.comment}} {{value.docComment}} {{/value.comment}} {{#value.isReadOnly}} @readOnly {{/value.isReadOnly}} {{value.type}} {{value.name}}{{value.tableName}} { {{#value.filteredFields}} {{#value.comment}} {{value.docComment}} {{/value.comment}} {{value.name}} {{value.typeName}}{{value.constraints}}{{^last}},{{/last}} {{/value.filteredFields}} } {{^last}}{{/last}} {{/value.isPureManyToMany}} {{/views}} {{^views}} // No views were found for which JDL is to be generated. Please review console logs {{/views}} ================================================ FILE: src/main/resources/mustache/viewsrelations.mustache ================================================ {{#viewsrelations}} {{#first}}// Relations{{/first}} {{#value.isPureManyToMany}} // TODO This is a pure ManyToMany relation (delete me and decide owner side) {{/value.isPureManyToMany}} {{#value.comment}} {{value.implComment}} {{/value.comment}} relationship {{value.type}} { {{^value.hasComments}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} {{#value.hasComments}} {{#value.ownerDocComment}} {{value.ownerDocComment}} {{/value.ownerDocComment}} {{value.ownerEntityName}}{{value.ownerConfig}} to {{#value.inverseDocComment}} {{value.inverseDocComment}} {{/value.inverseDocComment}} {{value.inverseSideEntityName}}{{value.inverseConfig}} {{/value.hasComments}} } {{^last}}{{/last}} {{/viewsrelations}} ================================================ FILE: src/main/resources/reserved/keywords.json ================================================ { "java": [ "ABSTRACT", "CONTINUE", "FOR", "NEW", "SWITCH", "ASSERT", "DEFAULT", "GOTO", "PACKAGE", "SYNCHRONIZED", "BOOLEAN", "DO", "IF", "PRIVATE", "THIS", "BREAK", "DOUBLE", "IMPLEMENTS", "PROTECTED", "THROW", "BYTE", "ELSE", "IMPORT", "PUBLIC", "THROWS", "CASE", "ENUM", "INSTANCEOF", "RETURN", "TRANSIENT", "CATCH", "EXTENDS", "INT", "SHORT", "TRY", "CHAR", "FINAL", "INTERFACE", "STATIC", "VOID", "CLASS", "FINALLY", "LONG", "STRICTFP", "VOLATILE", "CONST", "FLOAT", "NATIVE", "SUPER", "WHILE" ], "jhipster": [ "ACCOUNT", "ACTIVATE", "AUDITS", "CONFIGURATION", "DOCS", "HEALTH", "LOGS", "METRICS", "PASSWORD", "REGISTER", "RESET", "SESSIONS", "SETTINGS", "TEST", "EVENTMANAGER", "PRINCIPAL", "ENTITY", "RESULT" ], "angular": [ "CLASS", "NODENAME", "NODETYPE", "COMPONENT", "SUBSCRIPTION", "RESPONSE", "OBSERVABLE", "INJECTABLE", "HTTP", "ROUTER" ], "typescript": [ "BREAK", "CASE", "CATCH", "CLASS", "CONST", "CONSTRUCTOR", "CONTINUE", "DEBUGGER", "DEFAULT", "DELETE", "DO", "ELSE", "ENUM", "EXPORT", "EXTENDS", "FALSE", "FINALLY", "FOR", "FUNCTION", "IF", "IMPORT", "IN", "INSTANCEOF", "NEW", "NULL", "RETURN", "SUPER", "SWITCH", "THIS", "THROW", "TRUE", "TRY", "TYPEOF", "VAR", "VOID", "WHILE", "WITH", "IMPLEMENTS", "INTERFACE", "LET", "PACKAGE", "PRIVATE", "PROTECTED", "PUBLIC", "STATIC", "YIELD" ] } ================================================ FILE: src/main/resources/sql/mysql_mariadb-getAllTableInformation.sql ================================================ select t.table_schema, t.table_name, case when v.is_updatable='NO' then false else true end as is_updatable, case when t.table_type='VIEW' and t.table_comment='VIEW' then 'view' else t.table_comment end as comment, t.table_type from information_schema.tables t left join information_schema.views v on v.table_schema=t.table_schema and v.table_name=t.table_name where t.table_schema=:schemaName order by t.table_schema, v.is_updatable, t.table_name; ================================================ FILE: src/main/resources/sql/mysql_mariadb-getAllTableRelationInformation.sql ================================================ select kku.constraint_name, kku.table_schema, kku.table_name, kku.column_name, kku.referenced_table_schema as foreign_table_schema, kku.referenced_table_name as foreign_table_name, kku.referenced_column_name as foreign_column_name from information_schema.key_column_usage kku where kku.referenced_table_schema=:schemaName and kku.referenced_table_name is not null order by kku.table_name, kku.column_name; ================================================ FILE: src/main/resources/sql/mysql_mariadb-getFullColumnInformationOfTable.sql ================================================ select c.table_schema, c.table_name, c.column_name, case when c.character_maximum_length is null then c.data_type else concat(c.data_type, '(', c.character_maximum_length, ')') end as data_type, c.data_type, c.column_default, c.is_nullable, c.column_comment comment, c.column_key 'key', c.ordinal_position from information_schema.columns c where c.table_schema=:schemaName and c.table_name=:tableName order by c.ordinal_position; ================================================ FILE: src/main/resources/sql/oracle-getAllTableInformation.sql ================================================ select o.OWNER as schema, o.OBJECT_NAME as table_name, case WHEN v.READ_ONLY = 'Y' THEN 'NO' when v.READ_ONLY = 'N' or v.READ_ONLY is null THEN 'YES' END as is_updatable, case when (o.OBJECT_TYPE = 'VIEW' and tc.COMMENTS is null) then 'view' else tc.COMMENTS end "comment", case o.OBJECT_TYPE when 'VIEW' then 'VIEW' when 'TABLE' then 'BASE TABLE' end table_type from ALL_OBJECTS o LEFT JOIN ALL_TABLES t on (o.OWNER = t.OWNER and o.OBJECT_NAME = t.TABLE_NAME and o.OBJECT_TYPE = 'TABLE' AND t.NESTED = 'NO') LEFT JOIN ALL_VIEWS v on (o.OWNER = v.OWNER and o.OBJECT_NAME = v.VIEW_NAME and o.OBJECT_TYPE = 'VIEW') LEFT JOIN ALL_TAB_COMMENTS tc ON (o.OWNER = tc.OWNER AND o.OBJECT_NAME = tc.TABLE_NAME AND o.OBJECT_TYPE = tc.TABLE_TYPE) WHERE o.owner =:schemaName and (o.OBJECT_TYPE = 'VIEW' or o.OBJECT_TYPE = 'TABLE') order by t.OWNER, is_updatable, o.OBJECT_NAME ================================================ FILE: src/main/resources/sql/oracle-getAllTableRelationInformation.sql ================================================ SELECT a.constraint_name, c.owner as table_schema, a.table_name, LOWER(a.column_name) as column_name, -- referenced pk c_pk.OWNER foreign_table_schema, c_pk.table_name foreign_table_name, b.COLUMN_NAME foreign_column_name FROM all_cons_columns a JOIN all_constraints c ON a.owner = c.owner AND a.constraint_name = c.constraint_name JOIN all_constraints c_pk ON c.r_owner = c_pk.owner AND c.r_constraint_name = c_pk.constraint_name JOIN all_cons_columns b ON b.OWNER = c_pk.OWNER AND b.constraint_name = c_pk.constraint_name WHERE c.constraint_type = 'R' AND A.OWNER =:schemaName ================================================ FILE: src/main/resources/sql/oracle-getFullColumnInformationOfTable.sql ================================================ select a.OWNER as table_schema, a.TABLE_NAME, LOWER(a.COLUMN_NAME) as column_name, a.CHAR_LENGTH, case -- enum when (select pcc.COLUMN_NAME from all_constraints pc, all_cons_columns pcc where pcc.column_name = a.column_name and pcc.constraint_name = pc.constraint_name and pc.constraint_type in ('C') and lower(pc.SEARCH_CONDITION_VC) like '%in%' and pcc.owner = upper(a.OWNER) and pcc.table_name = upper(a.TABLE_NAME)) is not null then 'enum' -- NUMBER when a.data_type = 'NUMBER' and a.DATA_PRECISION > 0 and a.DATA_SCALE > 0 then 'NUMBER' || '(' || a.DATA_PRECISION || ',' || a.DATA_SCALE || ')' when a.data_type = 'NUMBER' and a.DATA_PRECISION > 0 then 'NUMBER' || '(' || a.DATA_PRECISION || ')' -- CHAR, VARCHAR2, NCHAR, NVARCHAR2 when (a.data_type in ('CHAR', 'VARCHAR2', 'NCHAR', 'NVARCHAR2')) and a.CHAR_LENGTH > 0 then a.data_type || '(' || a.CHAR_LENGTH || ')' -- FLOAT when a.data_type = 'FLOAT' and a.DATA_PRECISION > 0 then a.data_type || '(' || a.DATA_PRECISION || ')' else a.DATA_TYPE end data_type, null as column_default, DECODE(a.NULLABLE, 'Y', 'YES', 'N', 'NO') is_nullable, c.COMMENTS as "COMMENT", DECODE((select pc.CONSTRAINT_TYPE from all_constraints pc, all_cons_columns pcc where pcc.column_name = a.column_name and pcc.constraint_name = pc.constraint_name and pc.constraint_type in ('P', 'U') and pcc.owner = upper(a.OWNER) and pcc.table_name = upper(a.TABLE_NAME)), 'P', 'PRI', 'U', 'UNI') KEY, a.COLUMN_ID as ordinal_position from all_tab_columns a, ALL_TAB_COMMENTS b, ALL_COL_COMMENTS c WHERE a.OWNER =:schemaName AND A.TABLE_NAME =:tableName AND A.OWNER = B.OWNER AND A.OWNER = c.OWNER AND A.TABLE_NAME = B.TABLE_NAME AND A.TABLE_NAME = c.TABLE_NAME AND A.COLUMN_NAME = c.COLUMN_NAME ORDER BY a.COLUMN_ID ================================================ FILE: src/main/resources/sql/postgresql-getAllTableInformation.sql ================================================ with w_tables as ( select t.table_schema, t.table_name, t.is_insertable_into as is_updatable, obj_description(c.oid, 'pg_class') as table_comment, t.table_type from information_schema.tables t join pg_catalog.pg_class c on c.oid = cast(concat(t.table_schema, '.', t.table_name) as regclass) where t.table_schema=:schemaName order by t.table_schema, t.table_type, t.table_name ) select w.*, case when w.table_type='VIEW' and w.table_comment is null then 'view' else w.table_comment end as comment from w_tables w; ================================================ FILE: src/main/resources/sql/postgresql-getAllTableRelationInformation.sql ================================================ select tc.constraint_name, tc.table_schema, tc.table_name, kcu.column_name, ccu.table_schema as foreign_table_schema, ccu.table_name as foreign_table_name, ccu.column_name as foreign_column_name from information_schema.table_constraints as tc join information_schema.key_column_usage as kcu on tc.constraint_name = kcu.constraint_name and tc.table_schema = kcu.table_schema and tc.table_name=kcu.table_name join information_schema.constraint_column_usage as ccu on ccu.constraint_name = tc.constraint_name and ccu.table_schema = tc.table_schema where tc.constraint_type = 'FOREIGN KEY' and tc.table_schema=:schemaName order by tc.table_schema, tc.table_name, kcu.column_name; ================================================ FILE: src/main/resources/sql/postgresql-getFullColumnInformationOfTable.sql ================================================ with w_columns as ( select c.table_schema, c.table_name, c.column_name, case when c.data_type='USER-DEFINED' then 'enum('||c.udt_name||')' when c.character_maximum_length is null then c.data_type else c.data_type || '(' || c.character_maximum_length || ')' end as data_type, c.column_default, case when vsc.is_nullable is null then c.is_nullable else vsc.is_nullable end as is_nullable, pg_catalog.col_description(cl.oid, c.ordinal_position) as comment, c.ordinal_position from information_schema.columns c join pg_catalog.pg_class cl on cl.oid=concat(c.table_schema, '.', c.table_name)::regclass::oid left join information_schema.view_column_usage vcu on vcu.table_schema=c.table_schema and vcu.view_name=c.table_name and vcu.column_name=c.column_name left join information_schema.columns vsc on vsc.table_schema=vcu.table_schema and vsc.table_name=vcu.table_name and vsc.column_name=vcu.column_name where c.table_schema=:schemaName order by c.table_schema, c.table_name, c.ordinal_position ), w_constraints as ( select distinct -- Duplicates come from joining information_schema.view_column_usage. Not sure it this is even useful. c.table_schema, c.table_name, c.column_name, left(tc.constraint_type,3) as key from information_schema.columns c join information_schema.constraint_column_usage cu on cu.column_name = c.column_name join information_schema.table_constraints tc on tc.table_schema=c.table_schema and tc.table_name=c.table_name and cu.constraint_name = tc.constraint_name and tc.constraint_type in ('UNIQUE', 'PRIMARY KEY') where c.table_schema=:schemaName order by c.table_schema, c.table_name, c.column_name ) select distinct -- Duplicates come from joining information_schema.view_column_usage. Not sure it this is even useful. cd.table_schema, cd.table_name, cd.column_name, cd.data_type, cd.column_default, cd.is_nullable, cd.comment, cc.key, cd.ordinal_position from w_columns cd left join w_constraints cc on cc.table_schema=cd.table_schema and cc.table_name=cd.table_name and cc.column_name=cd.column_name where cd.table_schema=:schemaName and cd.table_name=:tableName order by cd.table_schema, cd.table_name, cd.ordinal_position; ================================================ FILE: src/main/resources/sql/sqlserver-getAllTableInformation.sql ================================================ WITH w_tables AS ( SELECT t.TABLE_SCHEMA, t.TABLE_NAME, t.TABLE_TYPE, CASE WHEN t.TABLE_TYPE = 'BASE TABLE' THEN 1 ELSE 0 END AS updateable, ep.value AS table_comment FROM INFORMATION_SCHEMA.TABLES t LEFT JOIN sys.tables st ON st.name = t.TABLE_NAME LEFT JOIN sys.extended_properties ep ON ep.major_id = st.object_id AND ep.minor_id = 0 AND ep.name = 'MS_Description' WHERE t.TABLE_SCHEMA = 'dbo' --ORDER BY -- t.TABLE_SCHEMA, -- t.TABLE_TYPE, -- t.TABLE_NAME; ) SELECT w.*, CASE WHEN w.TABLE_TYPE = 'VIEW' AND w.table_comment IS NULL THEN 'view' ELSE w.table_comment END AS comment FROM w_tables w --ORDER BY -- w.TABLE_SCHEMA, w.TABLE_TYPE, w.TABLE_NAME; ================================================ FILE: src/main/resources/sql/sqlserver-getAllTableRelationInformation.sql ================================================ -- create sqlserver-getAllTableRelationInformation.sql for mssql server database using example file postgresql-getAllTableRelationInformation.sql as for postregsql database SELECT FK.TABLE_SCHEMA AS table_schema, FK.TABLE_NAME AS table_name, CU.COLUMN_NAME AS column_name, PK.TABLE_SCHEMA AS foreign_table_schema, PK.TABLE_NAME AS foreign_table_name, PT.COLUMN_NAME AS foreign_column_name FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME INNER JOIN ( SELECT i1.TABLE_NAME, i2.COLUMN_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' ) PT ON PT.TABLE_NAME = PK.TABLE_NAME WHERE FK.TABLE_SCHEMA = :schemaName --ORDER BY -- FK.TABLE_SCHEMA, -- FK.TABLE_NAME; ================================================ FILE: src/main/resources/sql/sqlserver-getFullColumnInformationOfTable.sql ================================================ WITH w_columns AS ( SELECT c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME, CASE WHEN c.DATA_TYPE = 'USER-DEFINED' THEN 'enum(' + c.COLUMN_NAME + ')' WHEN c.CHARACTER_MAXIMUM_LENGTH IS NULL THEN c.DATA_TYPE ELSE c.DATA_TYPE + '(' + CAST(c.CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')' END AS data_type, c.COLUMN_DEFAULT, c.IS_NULLABLE, c.ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_SCHEMA = :schemaName ), w_constraints AS ( SELECT DISTINCT c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME, LEFT(tc.CONSTRAINT_TYPE, 3) AS keye FROM INFORMATION_SCHEMA.COLUMNS c JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu ON cu.COLUMN_NAME = c.COLUMN_NAME JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc ON tc.TABLE_SCHEMA = c.TABLE_SCHEMA AND tc.TABLE_NAME = c.TABLE_NAME AND cu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME AND tc.CONSTRAINT_TYPE IN ('UNIQUE', 'PRIMARY KEY') WHERE c.TABLE_SCHEMA = :schemaName ) SELECT * FROM w_columns cd left join w_constraints cc on cc.table_schema=cd.table_schema and cc.table_name=cd.table_name and cc.column_name=cd.column_name where cd.table_schema=:schemaName and cd.table_name=:tableName ================================================ FILE: src/test/java/org/blackdread/sqltojava/AssertUtil.java ================================================ package org.blackdread.sqltojava; import java.util.Collection; import java.util.Iterator; import org.junit.jupiter.api.Assertions; public final class AssertUtil { public static void assertFileSame(final Collection expected, final Collection actual) { if (expected.size() != actual.size()) { Assertions.fail(String.format("Expected (%s) and actual (%s) size are different", expected.size(), actual.size())); } if (expected.isEmpty()) { return; } final Iterator expectedIterator = expected.iterator(); final Iterator actualIterator = actual.iterator(); for (int i = 0; i < expected.size(); i++) { final String expectedNext = expectedIterator.next(); final String actualNext = actualIterator.next(); if (!expected.equals(actual)) { Assertions.fail(String.format("Line %d (%s) is different from expected (%s)", i, actualNext, expectedNext)); } } } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/FileUtil.java ================================================ package org.blackdread.sqltojava; import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; public final class FileUtil { public static List readAllLinesClasspath(final String name) throws URISyntaxException, IOException { return Files.readAllLines(Paths.get(FileUtil.class.getResource(name).toURI()), StandardCharsets.UTF_8); } public static List readAllLines(final String name) throws URISyntaxException, IOException { return Files.readAllLines(Paths.get(name), StandardCharsets.UTF_8); } /* try (InputStream in = this.getClass().getResourceAsStream("/result/mysql57-expected.jdl"); InputStreamReader fin = new InputStreamReader(in, StandardCharsets.UTF_8); BufferedReader bufIn = new BufferedReader(fin)) { } catch (Exception e) { throw new RuntimeException(e); } */ } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/LoggingExtension.java ================================================ package org.blackdread.sqltojava.shared; import org.blackdread.sqltojava.shared.interfaces.LoggingTest; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.slf4j.LoggerFactory; public class LoggingExtension implements TestInstancePostProcessor { @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) { LoggingTest loggingTest = (LoggingTest) testInstance; loggingTest.logger(LoggerFactory.getLogger(context.getTestClass().get())); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/MainApplicationContextLoader.java ================================================ package org.blackdread.sqltojava.shared; import org.blackdread.sqltojava.util.AppUtil; import org.springframework.boot.SpringApplication; import org.springframework.boot.test.context.SpringBootContextLoader; /** * Using this context loader is the same as what is loaded in main method via * the AppUtil.setup method. */ public class MainApplicationContextLoader extends SpringBootContextLoader { @Override protected SpringApplication getSpringApplication() { return AppUtil.setup(super.getSpringApplication()); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/interfaces/CompareJdlResultsTest.java ================================================ package org.blackdread.sqltojava.shared.interfaces; import java.io.IOException; import java.net.URISyntaxException; import java.util.List; import org.blackdread.sqltojava.AssertUtil; import org.blackdread.sqltojava.FileUtil; import org.junit.jupiter.api.Test; public interface CompareJdlResultsTest extends LoggingTest, EnvironmentTest { @Test default void testFileSame() throws URISyntaxException, IOException { String expectedResultPath = env().getProperty("expected.result.path"); String applicationExportPath = env().getProperty("application.export.path"); log().info("expected.result.path: " + expectedResultPath); log().info("application.export.path: " + applicationExportPath); final List expected = FileUtil.readAllLinesClasspath(expectedResultPath); final List actual = FileUtil.readAllLines(applicationExportPath); AssertUtil.assertFileSame(expected, actual); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/interfaces/ContainersStartedTest.java ================================================ package org.blackdread.sqltojava.shared.interfaces; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public interface ContainersStartedTest extends JdbcContainerTest { @Test default void testContainerRunning() { boolean test = container().isRunning(); String message = String.format("Container %s is not running", container().getDockerImageName()); assertTrue(test, message); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/interfaces/EnvironmentTest.java ================================================ package org.blackdread.sqltojava.shared.interfaces; import org.springframework.core.env.Environment; public interface EnvironmentTest { Environment env(); } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/interfaces/JdbcContainerTest.java ================================================ package org.blackdread.sqltojava.shared.interfaces; import org.testcontainers.containers.JdbcDatabaseContainer; public interface JdbcContainerTest { JdbcDatabaseContainer container(); void container(JdbcDatabaseContainer container); } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/interfaces/LoggingTest.java ================================================ package org.blackdread.sqltojava.shared.interfaces; import org.slf4j.Logger; public interface LoggingTest extends JdbcContainerTest { Logger log(); void logger(Logger log); } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/interfaces/ProfileActiveTest.java ================================================ package org.blackdread.sqltojava.shared.interfaces; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public interface ProfileActiveTest extends EnvironmentTest { @Test default void testCorrectProfileLoaded() { String expectedProfile = env().getProperty("expected.profile"); assertNotNull("expected.profile should not be null.", expectedProfile); String[] activeProfiles = env().getActiveProfiles(); String[] expectedProfiles = new String[] { expectedProfile }; Assertions.assertArrayEquals(expectedProfiles, activeProfiles, "Incorrect profile set"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/tests/BaseJdbcContainerTest.java ================================================ package org.blackdread.sqltojava.shared.tests; import org.blackdread.sqltojava.shared.LoggingExtension; import org.blackdread.sqltojava.shared.MainApplicationContextLoader; import org.blackdread.sqltojava.shared.interfaces.EnvironmentTest; import org.blackdread.sqltojava.shared.interfaces.JdbcContainerTest; import org.blackdread.sqltojava.shared.interfaces.LoggingTest; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.MSSQLServerContainer; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.OracleContainer; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Testcontainers; @Testcontainers @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @ExtendWith(LoggingExtension.class) @ContextConfiguration(loader = MainApplicationContextLoader.class) public abstract class BaseJdbcContainerTest implements LoggingTest, EnvironmentTest, JdbcContainerTest { private static Logger log; private static JdbcDatabaseContainer container; // EnvironmentTest @Autowired private Environment env; public static JdbcDatabaseContainer setupContainer(JdbcDatabaseContainer container) { BaseJdbcContainerTest.container = container; System.setProperty("spring.datasource.url", container.getJdbcUrl()); System.setProperty("spring.datasource.username", container.getUsername()); System.setProperty("spring.datasource.password", container.getPassword()); System.setProperty("application.database-to-export", getDefaultSchema(container)); return container; } /** * Get the default schema based on JdbcDatabaseContainer type. OracleContainer and MSSQLServerContainer have not been tested. * @param c * @return * @throws IllegalArgumentException */ private static String getDefaultSchema(JdbcDatabaseContainer c) throws IllegalArgumentException { //TODO use Pattern matching for switch when out of preview if (c instanceof MySQLContainer || c instanceof MariaDBContainer) { return "test"; } else if (c instanceof OracleContainer) { return "TEST"; } else if (c instanceof PostgreSQLContainer) { return "public"; } else if (c instanceof MSSQLServerContainer) { return "dbo"; } else { throw new IllegalArgumentException("Unrecognized JdbcDatabaseContainer " + container.getClass()); } } @Override public Environment env() { return env; } // LoggingTest @Override public Logger log() { return log; } @Override public void logger(Logger log) { BaseJdbcContainerTest.log = log; } // JdbcContainerTest @Override public JdbcDatabaseContainer container() { return container; } @Override public void container(JdbcDatabaseContainer container) { BaseJdbcContainerTest.container = container; } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/tests/SqlToJdlTransactionPerTestTest.java ================================================ package org.blackdread.sqltojava.shared.tests; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.SQLException; import java.util.stream.Stream; import liquibase.exception.LiquibaseException; import org.blackdread.sqltojava.service.logic.ExportService; import org.blackdread.sqltojava.service.logic.JdlService; import org.blackdread.sqltojava.shared.interfaces.ProfileActiveTest; import org.blackdread.sqltojava.util.ResourceUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; public abstract class SqlToJdlTransactionPerTestTest extends TransactionPerTestTest implements ProfileActiveTest { @Autowired private JdlService jdlService; @Autowired private ExportService exportService; /** * To run a different set, implement this method in subclasses. * @return list of test names to run */ private static Stream provideTestNames() { return Stream.of( "uuid_id_required", "all_types", "one_to_one", "display_field_many_to_one", "many_to_one", "one_to_one_main_map", "parent_child", "unique", "enum", "views", "readonly", "reflexive_relationship", "prune", "duplicate_names" ); } /** * Clear all caches so each test will generate the correct JDL */ @BeforeEach public void checkSchemaEmpty() { evictAllCaches(); } /** * This method will run a liquibase changeset, generate jdl, compare it to the expected results, * and then rollback to an empty database before running the next test. * @param testName * @throws SQLException * @throws LiquibaseException */ @ParameterizedTest @MethodSource("provideTestNames") public void testChangelog(String testName) { testJdl(testName); } private void testJdl(String testName) { String activeProfile = env().getActiveProfiles()[0]; String pathChangeLogFile = getExpectedLiquibaseChangeset(testName, activeProfile); try { runChangeLogFile(pathChangeLogFile); } catch (SQLException e) { throw new RuntimeException(e); } catch (LiquibaseException e) { throw new RuntimeException(e); } String jdlExpected = getExpectedJdl(testName, activeProfile); String jdlActual = exportService.exportString(jdlService.buildEntities()); assertThat(jdlActual).isEqualTo(jdlExpected).withFailMessage("JDL output did not match expected."); } protected void testJdlException(String testName, Class exceptionClass) { Exception exception = assertThrows( exceptionClass, () -> { testJdl(testName); } ); String expectedMessagePrefix = "Unsupported jdl type"; String actualMessage = exception.getCause().getMessage(); assertTrue(actualMessage.startsWith(expectedMessagePrefix), actualMessage); } /** * The expected jdl file will first be searched for at /jdl/testName-liquibase-changeset-activeProfile.yaml and then at * /jdl/testName-liquibase-changeset.yaml. If neither match an error will be thrown. * * @param testName * @param activeProfile * @return */ private String getExpectedLiquibaseChangeset(String testName, String activeProfile) { String pathChangesetProfile = String.format("/jdl/%s-liquibase-changeset-%s.yaml", testName, activeProfile); String pathChangesetDefault = String.format("/jdl/%s-liquibase-changeset.yaml", testName); return ResourceUtil.getFirstExistingResourcePath("liquibase-changeset.yaml", pathChangesetProfile, pathChangesetDefault); } /** * The expected jdl file will first be searched for at /jdl/testName-expected-activeProfile.jdl and then at * /jdl/testName-expected.jdl. If neither match an error will be thrown. * * @param testName * @param activeProfile * @return */ private String getExpectedJdl(String testName, String activeProfile) { String pathJdlExpectedProfile = String.format("/jdl/%s-expected-%s.jdl", testName, activeProfile); String pathJdlExpectedDefault = String.format("/jdl/%s-expected.jdl", testName); String pathJdlExpected = ResourceUtil.getFirstExistingResourcePath("expected.jdl", pathJdlExpectedProfile, pathJdlExpectedDefault); return pathToString(pathJdlExpected); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/shared/tests/TransactionPerTestTest.java ================================================ package org.blackdread.sqltojava.shared.tests; import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import liquibase.Contexts; import liquibase.LabelExpression; import liquibase.Liquibase; import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; import liquibase.exception.LiquibaseException; import liquibase.resource.ClassLoaderResourceAccessor; import liquibase.resource.ResourceAccessor; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.test.annotation.DirtiesContext; @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public abstract class TransactionPerTestTest extends BaseJdbcContainerTest { private static final Logger log = LoggerFactory.getLogger(TransactionPerTestTest.class); private static final String rollbackTag = "0.0.0"; private static final Contexts contexts = new Contexts("integration-test"); private static final LabelExpression labelExpression = new LabelExpression(); private static final ResourceAccessor RESOURCE_ACCESSOR = new ClassLoaderResourceAccessor(); private Liquibase liquibase; @Autowired private DataSource dataSource; @Autowired CacheManager cacheManager; private Connection connection; public DataSource dataSource() { return this.dataSource; } @AfterEach public void rollback() throws LiquibaseException { if (liquibase != null) { rollbackToEmpty(); liquibase.close(); } } @AfterAll public static void cleanup() {} protected String pathToString(String path) { String content = null; try { content = Files.readString(Path.of(getClass().getResource(path).toURI())); } catch (Exception e) {} return content; } protected void evictAllCaches() { log.info("Clearing all caches"); cacheManager.getCacheNames().stream().forEach(cacheName -> cacheManager.getCache(cacheName).clear()); } private Database database(DataSource dataSource) throws LiquibaseException { this.connection = DataSourceUtils.getConnection(dataSource); Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(this.connection)); return database; } protected void runChangeLogFile(String pathChangeLogFile) throws SQLException, LiquibaseException { liquibase = createLiquibase(pathChangeLogFile); liquibase.update(contexts, labelExpression); } private Liquibase createLiquibase(String pathChangeLogFile) throws SQLException, LiquibaseException { return new Liquibase(pathChangeLogFile, RESOURCE_ACCESSOR, database(dataSource)); } private void rollbackToEmpty() throws LiquibaseException { liquibase.rollback(rollbackTag, contexts); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/mssql/MssqlSql2019Test.java ================================================ package org.blackdread.sqltojava.test.db.mssql; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.MSSQLServerContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class MssqlSql2019Test extends SqlToJdlTransactionPerTestTest { @Container private static final MSSQLServerContainer MSSQL_CONTAINER = new MSSQLServerContainer( DockerImageName.parse("mcr.microsoft.com/mssql/server").withTag("2019-latest") ) .acceptLicense(); @SystemStub private static EnvironmentVariables env; protected static void setEnv(String name, String value) { env.set(name, value); } @BeforeAll public static void setup() { setEnv("expected.profile", "sqlserver"); setupContainer(MSSQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/mysql/MariaDBLatestTest.java ================================================ package org.blackdread.sqltojava.test.db.mysql; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class MariaDBLatestTest extends SqlToJdlTransactionPerTestTest { @Container private static final MariaDBContainer MARIA_DB_CONTAINER = new MariaDBContainer(DockerImageName.parse("mariadb:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "mariadb"); setupContainer(MARIA_DB_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/mysql/Mysql57Test.java ================================================ package org.blackdread.sqltojava.test.db.mysql; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class Mysql57Test extends SqlToJdlTransactionPerTestTest { @Container private static final MySQLContainer MYSQL_CONTAINER = new MySQLContainer(DockerImageName.parse("mysql:5.7")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "mysql"); setupContainer(MYSQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/mysql/Mysql8Test.java ================================================ package org.blackdread.sqltojava.test.db.mysql; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class Mysql8Test extends SqlToJdlTransactionPerTestTest { @Container private static final MySQLContainer MYSQL_CONTAINER = new MySQLContainer(DockerImageName.parse("mysql:8.0")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "mysql"); setupContainer(MYSQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/mysql/Mysql9Test.java ================================================ package org.blackdread.sqltojava.test.db.mysql; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @Disabled("Fails to connect to the container") @ExtendWith(SystemStubsExtension.class) class Mysql9Test extends SqlToJdlTransactionPerTestTest { @Container private static final MySQLContainer MYSQL_CONTAINER = new MySQLContainer(DockerImageName.parse("mysql:9")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "mysql"); setupContainer(MYSQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/mysql/MysqlLatestTest.java ================================================ package org.blackdread.sqltojava.test.db.mysql; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @Disabled("Fails to download the container") @ExtendWith(SystemStubsExtension.class) class MysqlLatestTest extends SqlToJdlTransactionPerTestTest { @Container private static final MySQLContainer MYSQL_CONTAINER = new MySQLContainer(DockerImageName.parse("mysql:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "mysql"); setupContainer(MYSQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/oracle/OracleLatestTest.java ================================================ package org.blackdread.sqltojava.test.db.oracle; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.OracleContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class OracleLatestTest extends SqlToJdlTransactionPerTestTest { @Container private static final OracleContainer ORACLE_CONTAINER = new OracleContainer(DockerImageName.parse("gvenzl/oracle-xe:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "oracle"); setupContainer(ORACLE_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/Postgres09Test.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class Postgres09Test extends SqlToJdlTransactionPerTestTest { static String dockerImageName; @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:9")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/Postgres10Test.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class Postgres10Test extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:10-alpine")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/Postgres15Test.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class Postgres15Test extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:15-alpine")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/Postgres17Test.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class Postgres17Test extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:17-alpine")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/PostgresAddTableNameJdlTest.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import java.util.stream.Stream; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class PostgresAddTableNameJdlTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); env.set("application.add_table_name_jdl", "true"); setupContainer(POSTGRE_SQL_CONTAINER); } private static Stream provideTestNames() { return Stream.of("table_name"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/PostgresDatabaseObjectPrefixTest.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import java.util.stream.Stream; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class PostgresDatabaseObjectPrefixTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } private static Stream provideTestNames() { return Stream.of("prefix"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/PostgresLatestTest.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import java.util.stream.Stream; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class PostgresLatestTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } private static Stream provideTestNames_() { return Stream.of("one_to_one"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/db/postgres/PostgresOverrideJdlTypeTest.java ================================================ package org.blackdread.sqltojava.test.db.postgres; import java.util.stream.Stream; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.TestPropertySource; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) @TestPropertySource(properties = "application.jdl-type-overrides.numeric=FLOAT") class PostgresOverrideJdlTypeTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } private static Stream provideTestNames() { return Stream.of("override_jdl_types"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/general/DatabaseObjectPrefixTest.java ================================================ package org.blackdread.sqltojava.test.general; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.blackdread.sqltojava.util.JdlUtils; import org.junit.jupiter.api.Test; public class DatabaseObjectPrefixTest { @Test public void testPrefix() { String name = JdlUtils.getEntityName("t_test", List.of("t_")); assertThat(name).isEqualTo("Test"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/general/MustacheTest.java ================================================ package org.blackdread.sqltojava.test.general; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.*; import java.util.stream.Collectors; import org.blackdread.sqltojava.config.ApplicationProperties; import org.blackdread.sqltojava.config.UndefinedJdlTypeHandlingEnum; import org.blackdread.sqltojava.entity.JdlFieldEnum; import org.blackdread.sqltojava.entity.JdlRelation; import org.blackdread.sqltojava.entity.RelationType; import org.blackdread.sqltojava.entity.impl.JdlEntityImpl; import org.blackdread.sqltojava.entity.impl.JdlFieldImpl; import org.blackdread.sqltojava.entity.impl.JdlRelationImpl; import org.blackdread.sqltojava.service.logic.MustacheService; import org.blackdread.sqltojava.util.JdlUtils; import org.blackdread.sqltojava.view.impl.JdlRelationViewImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { MustacheTest.MustacheServiceTestContextConfiguration.class }) public class MustacheTest { @TestConfiguration static class MustacheServiceTestContextConfiguration { @Bean public MustacheService mustacheService() { ApplicationProperties properties = mock(ApplicationProperties.class); when(properties.getUndefinedTypeHandling()).thenReturn(UndefinedJdlTypeHandlingEnum.UNSUPPORTED); return new MustacheService(properties); } } @Autowired private MustacheService mustacheService; JdlFieldImpl p = new JdlFieldImpl(JdlFieldEnum.UUID, "id", true, null, null, null, null, null, false, false, true); JdlFieldImpl f = new JdlFieldImpl(JdlFieldEnum.STRING, "abrev", true, null, 2, 6, null, null, false, true, false); JdlEntityImpl b = new JdlEntityImpl("B", null, List.of(p, f), "Entity B comment", false, false, false, Collections.emptyList()); JdlRelationImpl relationWithComments = new JdlRelationViewImpl( RelationType.ManyToOne, true, true, false, "A", "B", "a", null, "Owner comment", "Inverse comment", "b", null, "Relation comment" ); JdlRelationImpl relationWithoutComments = new JdlRelationViewImpl( RelationType.ManyToOne, true, true, false, "A", "B", "a", null, null, null, "b", null, null ); JdlRelationImpl relationWithoutBirectional = new JdlRelationViewImpl( RelationType.ManyToOne, false, true, false, "A", "B", "a", null, null, null, "b", null, null ); JdlEntityImpl a = new JdlEntityImpl( "A", null, List.of(p, f), "Entity A comment", false, false, false, List.of(relationWithoutComments) ); @Test public void testEntity() { String expected = """ /** Entity A comment */ entity A { id UUID, abrev String required unique minlength(2) maxlength(6) } """; Map context = new HashMap<>(); context.put("entities", List.of(a)); String result = mustacheService.executeTemplate("entities", context); assertThat(result).isEqualTo(expected); } @Test public void testRelation() { String expected = """ // Relations // Relation comment relationship ManyToOne { /** Owner comment */ A{a required} to /** Inverse comment */ B{b} } """; String result = mustacheService.executeTemplate("relations", Map.of("relations", List.of(relationWithComments))); assertThat(result).isEqualTo(expected); } @Test public void testRelationWithoutComments() { String expected = """ // Relations relationship ManyToOne { A{a required} to B{b} } """; String result = mustacheService.executeTemplate("relations", Map.of("relations", List.of(relationWithoutComments))); assertThat(result).isEqualTo(expected); } @Test public void testRelationWithoutBidirectional() { String expected = """ // Relations relationship ManyToOne { A{a required} to B } """; String result = mustacheService.executeTemplate("relations", Map.of("relations", List.of(relationWithoutBirectional))); assertThat(result).isEqualTo(expected); } @Test public void testEntities() { String expected = """ /** Entity A comment */ entity A { id UUID, abrev String required unique minlength(2) maxlength(6) } /** Entity B comment */ entity B { id UUID, abrev String required unique minlength(2) maxlength(6) } """; List entities = Arrays.asList(a, b); Map context = new HashMap<>(); context.put("entities", entities); String result = mustacheService.executeTemplate("entities", context); assertThat(result).isEqualTo(expected); } @Test public void testNoEntities() { String expected = """ // No entities were found for which JDL is to be generated. Please review console logs """; List entities = Collections.emptyList(); Map context = new HashMap<>(); context.put("entities", entities); String result = mustacheService.executeTemplate("entities", context); assertThat(result).isEqualTo(expected); } @Test public void testApplication() { String expected = """ /** Entity A comment */ entity A { id UUID, abrev String required unique minlength(2) maxlength(6) } /** Entity B comment */ entity B { id UUID, abrev String required unique minlength(2) maxlength(6) } // Relations relationship ManyToOne { A{a required} to B{b} } """; List entities = Arrays.asList(a, b); List relations = entities.stream().flatMap(e -> e.getRelations().stream()).collect(Collectors.toList()); List options = JdlUtils.getOptions(); Map context = new HashMap<>(); context.put("entities", entities); context.put("relations", relations); String result = mustacheService.executeTemplate("application", context); assertThat(result).isEqualTo(expected); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/general/unsupported/PostgresUndefinedErrorTest.java ================================================ package org.blackdread.sqltojava.test.general.unsupported; import java.util.stream.Stream; import org.blackdread.sqltojava.config.UndefinedJdlTypeHandlingEnum; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class PostgresUndefinedErrorTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); env.set("application.undefined_type_handling", UndefinedJdlTypeHandlingEnum.ERROR); setupContainer(POSTGRE_SQL_CONTAINER); } @ParameterizedTest @MethodSource("provideTestNames") public void testChangelog(String testName) { testJdlException(testName, RuntimeException.class); } private static Stream provideTestNames() { return Stream.of("undefined_error"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/general/unsupported/PostgresUndefinedSkioTest.java ================================================ package org.blackdread.sqltojava.test.general.unsupported; import java.util.stream.Stream; import org.blackdread.sqltojava.config.UndefinedJdlTypeHandlingEnum; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class PostgresUndefinedSkioTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); env.set("application.undefined_type_handling", UndefinedJdlTypeHandlingEnum.SKIP); setupContainer(POSTGRE_SQL_CONTAINER); } private static Stream provideTestNames() { return Stream.of("undefined_skip"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/general/unsupported/PostgresUndefinedUnsupportedTest.java ================================================ package org.blackdread.sqltojava.test.general.unsupported; import java.util.stream.Stream; import org.blackdread.sqltojava.config.UndefinedJdlTypeHandlingEnum; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) class PostgresUndefinedUnsupportedTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); env.set("application.undefined_type_handling", UndefinedJdlTypeHandlingEnum.UNSUPPORTED); setupContainer(POSTGRE_SQL_CONTAINER); } private static Stream provideTestNames() { return Stream.of("undefined_unsupported"); } } ================================================ FILE: src/test/java/org/blackdread/sqltojava/test/general/views/PostgresDatabaseObjectTypesTest.java ================================================ package org.blackdread.sqltojava.test.general.views; import java.util.stream.Stream; import org.blackdread.sqltojava.shared.tests.SqlToJdlTransactionPerTestTest; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.TestPropertySource; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.utility.DockerImageName; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) @TestPropertySource(properties = "application.database_object_types_config=TABLES") class PostgresDatabaseObjectTypesConfigTest extends SqlToJdlTransactionPerTestTest { @Container private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer(DockerImageName.parse("postgres:latest")); @SystemStub private static EnvironmentVariables env; @BeforeAll public static void setup() { env.set("expected.profile", "postgresql"); setupContainer(POSTGRE_SQL_CONTAINER); } private static Stream provideTestNames() { return Stream.of("tables-only"); } } ================================================ FILE: src/test/resources/application.yml ================================================ spring: datasource: type: com.zaxxer.hikari.HikariDataSource #url: dynamically set based on JdbcDatabaseContainer type #username: dynamically set based on JdbcDatabaseContainer type #password: dynamically set based on JdbcDatabaseContainer type hikari: data-source-properties: cachePrepStmts: true prepStmtCacheSize: 250 prepStmtCacheSqlLimit: 2048 useServerPrepStmts: true flyway: enabled: false application: reserved-keywords: classpath:reserved/keywords.json #database-to-export: dynamically set based on JdbcDatabaseContainer type database-object-prefix: - t_ - v_ add_table_name_jdl: false undefined_type_handling: ERROR database_object_types_config: ALL # TABLES, ViEWS, ALL render_entities_only: true assume_bidirectional: true ignored-table-names: - databasechangelog - databasechangeloglock - DATABASECHANGELOG - DATABASECHANGELOGLOCK - flyway_schema_history - QRTZ_BLOB_TRIGGERS - QRTZ_CALENDARS - QRTZ_CRON_TRIGGERS - QRTZ_FIRED_TRIGGERS - QRTZ_JOB_DETAILS - QRTZ_LOCKS - QRTZ_PAUSED_TRIGGER_GRPS - QRTZ_SCHEDULER_STATE - QRTZ_SIMPLE_TRIGGERS - QRTZ_SIMPROP_TRIGGERS - QRTZ_TRIGGERS - MSreplication_options - spt_fallback_db - spt_fallback_dev - spt_fallback_usg - spt_monitor - spt_values export: path: ./my-project-jdl.jh type: jdl export-file-structure-type: SEPARATED export-mustache-template-filename-optional: ================================================ FILE: src/test/resources/container-license-acceptance.txt ================================================ mcr.microsoft.com/mssql/server:2019-latest ================================================ FILE: src/test/resources/db/changelog/db.changelog-master.yaml ================================================ databaseChangeLog: - changeSet: id: empty author: empty changes: - tagDatabase: tag: 0.0.0 - empty: { } ================================================ FILE: src/test/resources/jdl/all_jdl_types-expected.jdl ================================================ /** This comment will be taken into account */ enum EnumType { // But not this one! VALUE1, VALUE2 } entity AllType { myString String, myInteger Integer, myLong Long, myBigDecimal BigDecimal, myFloat Float, myDouble Double, myEnum Enum, myBoolean Boolean, myLocalDate LocalDate, myZonedDateTime ZonedDateTime, myInstant Instant, myDuration Duration, myUUID UUID, myBlob Blob, myAnyBlob AnyBlob, myImageBlob ImageBlob, myTextBlob TextBlob } ================================================ FILE: src/test/resources/jdl/all_types-expected-mariadb.jdl ================================================ /** my table comment */ entity AllType { myId String required maxlength(25), myInt Integer required, myIntNull Integer, myVarchar50 String required maxlength(50), myVarchar50Null String maxlength(50), /** super varchar 512 */ myVarchar512 String required maxlength(512), myTinytext String required maxlength(255), myMediumtext TextBlob required, myLongtext TextBlob required, myText String required maxlength(65535), myChar String required maxlength(5), myDate LocalDate required, myDatetime Instant required, myTimestamp Instant required, myTime String required minlength(8) maxlength(8) pattern(/^(([0-1]\d)|(2[0-3])):([0-5]\d):([0-5]\d)$/), myBigint Long required, myTinyint Boolean required, /** small int comment */ mySmallint Integer required, myDecimal BigDecimal required, myDouble Double required, myBit Boolean required, myBool Boolean required, myJson TextBlob required, myTinyblob Blob required, myMediumblob Blob required, myLongblob Blob required, myTinyint8 Boolean required } /** my table comment */ entity MysqlType { myYear String required pattern(/^-?(\d+)$/), myMediumint Integer required, myFloat Float required, myEnumSql MyEnumSql required, mySet MySet required, myGeometry String required, /** blob comment */ myBlob Blob required } ================================================ FILE: src/test/resources/jdl/all_types-expected-mysql.jdl ================================================ /** my table comment */ entity AllType { myId String required maxlength(25), myInt Integer required, myIntNull Integer, myVarchar50 String required maxlength(50), myVarchar50Null String maxlength(50), /** super varchar 512 */ myVarchar512 String required maxlength(512), myTinytext String required maxlength(255), myMediumtext TextBlob required, myLongtext TextBlob required, myText String required maxlength(65535), myChar String required maxlength(5), myDate LocalDate required, myDatetime Instant required, myTimestamp Instant required, myTime String required minlength(8) maxlength(8) pattern(/^(([0-1]\d)|(2[0-3])):([0-5]\d):([0-5]\d)$/), myBigint Long required, myTinyint Boolean required, /** small int comment */ mySmallint Integer required, myDecimal BigDecimal required, myDouble Double required, myBit Boolean required, myBool Boolean required, myJson String required, myTinyblob Blob required, myMediumblob Blob required, myLongblob Blob required, myTinyint8 Boolean required } /** my table comment */ entity MysqlType { myYear String required pattern(/^-?(\d+)$/), myMediumint Integer required, myFloat Float required, myEnumSql MyEnumSql required, mySet MySet required, myGeometry String required, /** blob comment */ myBlob Blob required } ================================================ FILE: src/test/resources/jdl/all_types-expected-oracle.jdl ================================================ /** my table comment */ entity OracleType { myId String required maxlength(25), myBoolean Integer, myTinyint Integer, myInt Integer, myBigint Long, myFloat Float, myDouble Float, myDecimal Integer, myNumber Integer, /** blob comment */ myBlob Blob, myDatetime Instant, myTime LocalDate, myTimestamp Instant, myDate LocalDate, myChar String maxlength(1), myVarchar50 String required maxlength(50), myVarchar50Null String maxlength(50), myNchar String maxlength(1), myNvarchar50 String required maxlength(50), myNvarchar50Null String maxlength(50), myClob Blob, myCurrency Integer, myUuid UUID } ================================================ FILE: src/test/resources/jdl/all_types-expected-postgresql.jdl ================================================ entity PostgresType { myBoolean Boolean, myDate LocalDate, myTime String minlength(8) maxlength(8) pattern(/^(([0-1]\d)|(2[0-3])):([0-5]\d):([0-5]\d)$/), myTimeWithTimeZone String minlength(8) maxlength(8) pattern(/^(([0-1]\d)|(2[0-3])):([0-5]\d):([0-5]\d)$/), myTimestamp Instant, myTimestampWithTimeZone ZonedDateTime, myReal Float, myDoublePrecision Double, mySmallint Integer, myInteger Integer, myBigint Long, myMoney BigDecimal, myNumeric BigDecimal, myBpchar String maxlength(1), myChar String maxlength(1), myText String, myVarchar String maxlength(255), myBytea Blob, myJson String, myUuid UUID } ================================================ FILE: src/test/resources/jdl/all_types-expected-sqlserver.jdl ================================================ /** my table comment */ entity AllType { myId String required maxlength(25), myInt Integer required, myIntNull Integer, myVarchar50 String required maxlength(50), myVarchar50Null String maxlength(50), myVarchar512 String required maxlength(512), myTinytext TextBlob required, myMediumtext TextBlob required, myLongtext TextBlob required, myText TextBlob required, myChar String required maxlength(5), myDate LocalDate required, myDatetime Instant required, myTimestamp Instant required, myTime String required minlength(8) maxlength(8) pattern(/^(([0-1]\d)|(2[0-3])):([0-5]\d):([0-5]\d)$/), myBigint Long required, myTinyint Integer required, mySmallint Integer required, myDecimal BigDecimal required, myDouble Double required, myBit Boolean required, myBool Boolean required, myJson TextBlob required, myTinyblob Blob required, myMediumblob Blob required, myLongblob Blob required, myTinyint8 Integer required } ================================================ FILE: src/test/resources/jdl/all_types-liquibase-changeset-sqlserver.yaml ================================================ databaseChangeLog: - changeSet: id: all_types-create_table_all author: Generated with liquibase:generate-changelog from flyway sql from existing tests # dbms: sqlserver changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: my_id type: NVARCHAR(25) - column: constraints: nullable: false defaultValueNumeric: 55 name: my_int type: INT - column: name: my_int_null type: INT - column: constraints: nullable: false defaultValue: default of varchar 50 name: my_varchar_50 type: NVARCHAR(50) - column: name: my_varchar_50_null type: NVARCHAR(50) - column: constraints: nullable: false name: my_varchar_512 remarks: super varchar 512 type: NVARCHAR(512) - column: constraints: nullable: false name: my_tinytext type: NVARCHAR(MAX) - column: constraints: nullable: false name: my_mediumtext type: NVARCHAR(max) - column: constraints: nullable: false name: my_longtext type: NVARCHAR(MAX) - column: constraints: nullable: false name: my_text type: NVARCHAR(MAX) - column: constraints: nullable: false name: my_char type: CHAR(5) - column: constraints: nullable: false name: my_date type: DATE - column: constraints: nullable: false name: my_datetime type: DATETIME - column: constraints: nullable: false name: my_timestamp type: DATETIME - column: constraints: nullable: false name: my_time type: TIME - column: constraints: nullable: false name: my_bigint type: BIGINT - column: constraints: nullable: false name: my_tinyint type: TINYINT - column: constraints: nullable: false name: my_smallint type: SMALLINT remarks: small int comment - column: constraints: nullable: false name: my_decimal type: DECIMAL(19, 5) - column: constraints: nullable: false name: my_double type: FLOAT - column: constraints: nullable: false name: my_bit type: BIT - column: constraints: nullable: false name: my_bool type: BIT - column: constraints: nullable: false name: my_json type: NVARCHAR(MAX) - column: constraints: nullable: false name: my_tinyblob type: VARBINARY(MAX) - column: constraints: nullable: false name: my_mediumblob type: VARBINARY(MAX) - column: constraints: nullable: false name: my_longblob type: VARBINARY(MAX) - column: constraints: nullable: false name: my_tinyint_8 type: TINYINT remarks: my table comment tableName: all_type ================================================ FILE: src/test/resources/jdl/all_types-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: all_types-create_table_all author: Generated with liquibase:generate-changelog from flyway sql from existing tests dbms: mysql,mariadb changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: my_id type: VARCHAR(25) - column: constraints: nullable: false defaultValueNumeric: 55 name: my_int type: INT - column: name: my_int_null type: INT - column: constraints: nullable: false defaultValue: default of varchar 50 name: my_varchar_50 type: VARCHAR(50) - column: name: my_varchar_50_null type: VARCHAR(50) - column: constraints: nullable: false name: my_varchar_512 remarks: super varchar 512 type: VARCHAR(512) - column: constraints: nullable: false name: my_tinytext type: TINYTEXT - column: constraints: nullable: false name: my_mediumtext type: MEDIUMTEXT - column: constraints: nullable: false name: my_longtext type: LONGTEXT - column: constraints: nullable: false name: my_text type: TEXT - column: constraints: nullable: false name: my_char type: CHAR(5) - column: constraints: nullable: false name: my_date type: date - column: constraints: nullable: false name: my_datetime type: datetime - column: constraints: nullable: false name: my_timestamp type: timestamp - column: constraints: nullable: false name: my_time type: time - column: constraints: nullable: false name: my_bigint type: BIGINT - column: constraints: nullable: false name: my_tinyint type: TINYINT(3) - column: constraints: nullable: false name: my_smallint remarks: small int comment type: SMALLINT - column: constraints: nullable: false name: my_decimal type: DECIMAL(19, 5) - column: constraints: nullable: false name: my_double type: DOUBLE - column: constraints: nullable: false name: my_bit type: BIT(1) - column: constraints: nullable: false name: my_bool type: BIT(1) - column: constraints: nullable: false name: my_json type: JSON - column: constraints: nullable: false name: my_tinyblob type: TINYBLOB - column: constraints: nullable: false name: my_mediumblob type: MEDIUMBLOB - column: constraints: nullable: false name: my_longblob type: LONGBLOB - column: constraints: nullable: false name: my_tinyint_8 type: TINYINT(3) remarks: my table comment tableName: all_type - changeSet: id: all_types-create_table_ansi_sql author: Generated with liquibase:generate-changelog from flyway sql from existing tests dbms: mysql,mariadb changes: - createTable: columns: - column: constraints: nullable: false name: my_year type: YEAR(4) - column: constraints: nullable: false name: my_mediumint type: MEDIUMINT - column: constraints: nullable: false name: my_float type: FLOAT(12) - column: constraints: nullable: false name: my_enum_sql type: ENUM('value_one','value_two','value_three') - column: constraints: nullable: false name: my_set type: SET('set_one','set_two','set_three') - column: constraints: nullable: false name: my_geometry type: GEOMETRY - column: constraints: nullable: false name: my_blob remarks: blob comment type: BLOB remarks: my table comment tableName: mysql_type - changeSet: id: all_types-create_table_postgres author: jason.long dbms: postgresql changes: - createTable: columns: - column: name: my_boolean type: boolean - column: name: my_date type: date - column: name: my_time type: time - column: name: my_time_with_time_zone type: time with time zone - column: name: my_timestamp type: timestamp - column: name: my_timestamp_with_time_zone type: timestamp with time zone - column: name: my_real type: real - column: name: my_double_precision type: double precision - column: name: my_smallint type: smallint - column: name: my_integer type: integer - column: name: my_bigint type: bigint - column: name: my_money type: money - column: name: my_numeric type: numeric - column: name: my_bpchar type: bpchar - column: name: my_char type: char - column: name: my_text type: text - column: name: my_varchar type: varchar - column: name: my_bytea type: bytea - column: name: my_json type: json - column: name: my_uuid type: uuid tableName: postgres_type - changeSet: id: all_types-create_table_oracle author: mhmmdd dbms: oracle changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: my_id type: VARCHAR(25) - column: name: my_boolean type: boolean - column: name: my_tinyint type: tinyint - column: name: my_int type: int - column: name: my_bigint type: bigint - column: name: my_float type: float - column: name: my_double type: double - column: name: my_decimal type: decimal - column: name: my_number type: number - column: name: my_blob remarks: blob comment type: blob - column: name: my_datetime type: datetime - column: name: my_time type: time - column: name: my_timestamp type: timestamp - column: name: my_date type: date - column: name: my_char type: char - column: constraints: nullable: false name: my_varchar_50 type: VARCHAR(50) - column: name: my_varchar_50_null type: VARCHAR(50) - column: name: my_nchar type: nchar - column: constraints: nullable: false name: my_nvarchar_50 type: NVARCHAR(50) - column: name: my_nvarchar_50_null type: NVARCHAR(50) - column: name: my_clob type: clob - column: name: my_currency type: currency - column: name: my_uuid type: uuid tableName: oracle_type remarks: my table comment ================================================ FILE: src/test/resources/jdl/display_field_many_to_one-expected-sqlserver.jdl ================================================ entity Player { name String required unique maxlength(255) } entity Team { abrev String required unique maxlength(4), name String required unique maxlength(255) } // Relations relationship ManyToOne { Player{team(abrev) required} to Team{player(name)} } ================================================ FILE: src/test/resources/jdl/display_field_many_to_one-expected.jdl ================================================ entity Player { /** Used as display field because it is the first unique not foreign key column */ name String required unique maxlength(255) } entity Team { /** Used as display field because it is the first unique not foreign key column */ abrev String required unique maxlength(4), name String required unique maxlength(255) } // Relations relationship ManyToOne { Player{team(abrev) required} to Team{player(name)} } ================================================ FILE: src/test/resources/jdl/display_field_many_to_one-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: create_table_team-1 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false unique: true name: abrev type: varchar(4) remarks: Used as display field because it is the first unique not foreign key column - column: constraints: nullable: false unique: true name: name type: varchar(255) tableName: team - changeSet: id: create_table_player-2 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false unique: true name: name type: varchar(255) remarks: Used as display field because it is the first unique not foreign key column - column: constraints: nullable: false name: team_id type: bigint tableName: player - changeSet: id: add_foreign_key_player_team-3 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: team_id baseTableName: player constraintName: fk_player_team deferrable: false initiallyDeferred: false referencedColumnNames: id referencedTableName: team validate: true ================================================ FILE: src/test/resources/jdl/duplicate_names-expected.jdl ================================================ entity Task { name String required maxlength(255), summary String maxlength(255), description String maxlength(255), priority String maxlength(255), status String maxlength(255) } entity Worker { name String required maxlength(255), details String maxlength(255) } // Relations relationship ManyToOne { Task{assignee} to Worker{task} } relationship ManyToOne { Task{author} to Worker{taskOfAuthor} } relationship ManyToOne { Task{someOtherAuthor} to Worker{taskOfSomeOtherAuthor} } relationship ManyToOne { Task{evenMoreAuthor} to Worker{taskOfEvenMoreAuthor} } ================================================ FILE: src/test/resources/jdl/duplicate_names-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: 1674449168689-1 author: Generated with liquibase:generate-changelog from maven changes: - createTable: columns: - column: autoIncrement: true constraints: nullable: false primaryKey: true primaryKeyName: task_pkey name: id type: BIGINT - column: constraints: nullable: false name: name type: VARCHAR(255) - column: name: summary type: VARCHAR(255) - column: name: description type: VARCHAR(255) - column: name: priority type: VARCHAR(255) - column: name: assignee_id type: BIGINT - column: name: author_id type: BIGINT - column: name: some_other_author_id type: BIGINT - column: name: even_more_author_id type: BIGINT - column: name: status type: VARCHAR(255) tableName: task - changeSet: id: 1674449168689-2 author: Generated with liquibase:generate-changelog from maven changes: - createTable: columns: - column: autoIncrement: true constraints: nullable: false primaryKey: true primaryKeyName: worker_pkey name: id type: BIGINT - column: constraints: nullable: false name: name type: VARCHAR(255) - column: name: details type: VARCHAR(255) tableName: worker - changeSet: id: 1674449168689-3 author: Generated with liquibase:generate-changelog from maven changes: - addForeignKeyConstraint: baseColumnNames: assignee_id baseTableName: task constraintName: task_assignee_id_fkey deferrable: false initiallyDeferred: false onDelete: NO ACTION onUpdate: NO ACTION referencedColumnNames: id referencedTableName: worker validate: true - changeSet: id: 1674449168689-4 author: Generated with liquibase:generate-changelog from maven changes: - addForeignKeyConstraint: baseColumnNames: author_id baseTableName: task constraintName: task_author_id_fkey deferrable: false initiallyDeferred: false onDelete: NO ACTION onUpdate: NO ACTION referencedColumnNames: id referencedTableName: worker validate: true - changeSet: id: 1674449168689-5 author: Generated with liquibase:generate-changelog from maven changes: - addForeignKeyConstraint: baseColumnNames: some_other_author_id baseTableName: task constraintName: task_some_other_author_id_fkey deferrable: false initiallyDeferred: false onDelete: NO ACTION onUpdate: NO ACTION referencedColumnNames: id referencedTableName: worker validate: true - changeSet: id: 1674449168689-6 author: Generated with liquibase:generate-changelog from maven changes: - addForeignKeyConstraint: baseColumnNames: even_more_author_id baseTableName: task constraintName: task_even_more_author_id_fkey deferrable: false initiallyDeferred: false onDelete: NO ACTION onUpdate: NO ACTION referencedColumnNames: id referencedTableName: worker validate: true ================================================ FILE: src/test/resources/jdl/enum-expected-sqlserver.jdl ================================================ entity City { id String required maxlength(25), name String maxlength(45), /** city_status comment */ cityStatus CityStatus required, cityType String maxlength(6) } /** city_status comment */ enum CityStatus { } ================================================ FILE: src/test/resources/jdl/enum-expected.jdl ================================================ entity City { id String required maxlength(25), name String maxlength(45), /** city_status comment */ cityStatus CityStatus required, /** native enum comment */ cityType CityType } /** city_status comment */ enum CityStatus { } ================================================ FILE: src/test/resources/jdl/enum-liquibase-changeset-sqlserver.yaml ================================================ databaseChangeLog: - changeSet: id: enum-create-table-city author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: NVARCHAR(25) - column: name: name type: NVARCHAR(45) - column: constraints: nullable: false name: city_status_id type: NVARCHAR(25) remarks: city_status comment tableName: city - changeSet: id: enum-add-field-sqlserver author: jason.long (generated) # dbms: sqlserver changes: - addColumn: tableName: city columns: - column: name: city_type remarks: native enum comment type: NVARCHAR(6) constraints: check: CITY_TYPE in ('small', 'big', 'mega') - changeSet: id: enum-create-table-city_status author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: NVARCHAR(25) - column: constraints: nullable: false unique: true name: name type: NVARCHAR(255) remarks: city_status comment tableName: city_status - changeSet: id: enum-add-idx_city_city_status author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createIndex: columns: - column: name: city_status_id indexName: fk_city_city_status1_idx tableName: city - changeSet: id: enum-add-fk_city_city_status author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: city_status_id baseTableName: city constraintName: fk_city_city_status1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: city_status validate: true ================================================ FILE: src/test/resources/jdl/enum-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: enum-create-table-city author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: name: name type: VARCHAR(45) - column: constraints: nullable: false name: city_status_id type: VARCHAR(25) tableName: city - changeSet: id: enum-add-field-mysql author: jason.long (generated) dbms: mysql,mariadb changes: - addColumn: tableName: city columns: - column: name: city_type remarks: native enum comment type: ENUM('small', 'big', 'mega') - changeSet: id: enum-add-enum-type-postgres author: jason.long dbms: postgresql changes: - sql: create type city_type as enum ('small', 'big', 'mega'); - rollback: drop type city_type; - changeSet: id: enum-add-field-oracle author: mhmmdd dbms: oracle changes: # create enum constraint # https://dba.stackexchange.com/a/81762 - sql: ALTER TABLE CITY ADD (CITY_TYPE VARCHAR2(6) CONSTRAINT city_type_value CHECK (CITY_TYPE in ('small', 'big', 'mega'))); COMMENT ON COLUMN CITY.CITY_TYPE is 'native enum comment' - rollback: ALTER TABLE CITY DROP COLUMN CITY_TYPE - changeSet: id: enum-add-field-postgres author: jason.long dbms: postgresql changes: - addColumn: tableName: city columns: - column: name: city_type remarks: native enum comment type: city_type - changeSet: id: enum-create-table-city_status author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false unique: true name: name type: VARCHAR(255) remarks: city_status comment tableName: city_status - changeSet: id: enum-add-idx_city_city_status author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createIndex: columns: - column: name: city_status_id indexName: fk_city_city_status1_idx tableName: city - changeSet: id: enum-add-fk_city_city_status author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: city_status_id baseTableName: city constraintName: fk_city_city_status1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: city_status validate: true ================================================ FILE: src/test/resources/jdl/many_to_one-expected.jdl ================================================ /** many_to_one_child comment */ entity ManyToOneChild { id String required maxlength(25), name String required maxlength(45), other String required maxlength(45) } /** many_to_one_main comment */ entity ManyToOneMain { id String required maxlength(25), name String required maxlength(45), other String required maxlength(45) } // Relations // many_to_one_main comment relationship ManyToOne { ManyToOneMain{manyToOneChild required} to ManyToOneChild{manyToOneMain} } ================================================ FILE: src/test/resources/jdl/many_to_one-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: many_to_one-1 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) remarks: many_to_one_child comment tableName: many_to_one_child - changeSet: id: many_to_one-2 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) - column: constraints: nullable: false name: many_to_one_child_id type: VARCHAR(25) remarks: many_to_one_main comment tableName: many_to_one_main - changeSet: id: many_to_one-3 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createIndex: columns: - column: name: many_to_one_child_id indexName: fk_many_to_one_main_many_to_one_child1_idx tableName: many_to_one_main - changeSet: id: many_to_one-4 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: many_to_one_child_id baseTableName: many_to_one_main constraintName: fk_many_to_one_main_many_to_one_child1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: many_to_one_child validate: true ================================================ FILE: src/test/resources/jdl/one_to_one-expected.jdl ================================================ entity OneToOneChild { id String required maxlength(25), name String required maxlength(45), other String required maxlength(45) } /** one_to_one_main comment */ entity OneToOneMain { id String required maxlength(25), name String required maxlength(45), other String required maxlength(45) } // Relations // one_to_one_main comment relationship OneToOne { OneToOneMain{oneToOneChild required} to OneToOneChild{oneToOneMain} } ================================================ FILE: src/test/resources/jdl/one_to_one-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: one_to_one-1 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) tableName: one_to_one_child - changeSet: id: one_to_one-2 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) - column: constraints: nullable: false unique: true name: one_to_one_child_id type: VARCHAR(25) remarks: one_to_one_main comment tableName: one_to_one_main - changeSet: id: one_to_one-3 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: one_to_one_child_id baseTableName: one_to_one_main constraintName: fk_one_to_one_main_one_to_one_child1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: one_to_one_child validate: true ================================================ FILE: src/test/resources/jdl/one_to_one_main_map-expected.jdl ================================================ entity OneToOneChildMap { name String required maxlength(45) } /** one_to_one_main_map comment */ entity OneToOneMainMap { id String required maxlength(25), name String required maxlength(45), other String required maxlength(45) } // Relations relationship ManyToOne { OneToOneChildMap{oneToOneMainMap required} to OneToOneMainMap{oneToOneChildMap} } ================================================ FILE: src/test/resources/jdl/one_to_one_main_map-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: one_to_one_main_map-1 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: one_to_one_main_map_id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) tableName: one_to_one_child_map - changeSet: id: one_to_one_main_map-2 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) remarks: one_to_one_main_map comment tableName: one_to_one_main_map - changeSet: id: one_to_one_main_map-3 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: one_to_one_main_map_id baseTableName: one_to_one_child_map constraintName: fk_one_to_one_child_map_one_to_one_main_map1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: one_to_one_main_map validate: true ================================================ FILE: src/test/resources/jdl/override_jdl_types-expected-postgresql.jdl ================================================ entity Override { /** This column should be overridden to JDL type Float from the default of BigDecimal. */ f Float required } ================================================ FILE: src/test/resources/jdl/override_jdl_types-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: override_jdl_types-create_table author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false name: f type: numeric remarks: This column should be overridden to JDL type Float from the default of BigDecimal. tableName: override ================================================ FILE: src/test/resources/jdl/parent_child-expected.jdl ================================================ entity Child { id String required maxlength(25), name String required maxlength(45), other String required maxlength(45) } entity Parent { id String required maxlength(25), name String required maxlength(45), other String required maxlength(45) } // Relations // TODO This is a pure ManyToMany relation (delete me and decide owner side) // parent_has_child comment relationship ManyToMany { ParentHasChild{parent required} to Parent{parentHasChild} } // TODO This is a pure ManyToMany relation (delete me and decide owner side) // parent_has_child comment relationship ManyToMany { ParentHasChild{child required} to Child{parentHasChild} } ================================================ FILE: src/test/resources/jdl/parent_child-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: parent_child-1 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) tableName: child - changeSet: id: parent_child-2 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: VARCHAR(25) - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) tableName: parent - changeSet: id: parent_child-3 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: parent_id type: VARCHAR(25) - column: constraints: nullable: false primaryKey: true name: child_id type: VARCHAR(25) remarks: parent_has_child comment tableName: parent_has_child - changeSet: id: parent_child-4 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createIndex: columns: - column: name: child_id indexName: fk_parent_has_child_child1_idx tableName: parent_has_child - changeSet: id: parent_child-5 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createIndex: columns: - column: name: parent_id indexName: fk_parent_has_child_parent1_idx tableName: parent_has_child - changeSet: id: parent_child-6 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: child_id baseTableName: parent_has_child constraintName: fk_parent_has_child_child1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: child validate: true - changeSet: id: parent_child-7 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: parent_id baseTableName: parent_has_child constraintName: fk_parent_has_child_parent1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: parent validate: true ================================================ FILE: src/test/resources/jdl/prefix-expected.jdl ================================================ /** many_to_one_child comment */ entity ManyToOneChild { name String required maxlength(45), other String required maxlength(45) } /** many_to_one_main comment */ entity ManyToOneMain { name String required maxlength(45), other String required maxlength(45) } // Relations // many_to_one_main comment relationship ManyToOne { ManyToOneMain{manyToOneChild required} to ManyToOneChild{manyToOneMain} } ================================================ FILE: src/test/resources/jdl/prefix-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: many_to_one_prefix-1 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) remarks: many_to_one_child comment tableName: t_many_to_one_child - changeSet: id: many_to_one-2 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false name: name type: VARCHAR(45) - column: constraints: nullable: false name: other type: VARCHAR(45) - column: constraints: nullable: false name: many_to_one_child_id type: bigint remarks: many_to_one_main comment tableName: t_many_to_one_main - changeSet: id: many_to_one-3 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createIndex: columns: - column: name: many_to_one_child_id indexName: fk_many_to_one_main_many_to_one_child1_idx tableName: t_many_to_one_main - changeSet: id: many_to_one-4 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - addForeignKeyConstraint: baseColumnNames: many_to_one_child_id baseTableName: t_many_to_one_main constraintName: fk_many_to_one_main_many_to_one_child1 deferrable: false initiallyDeferred: false onDelete: RESTRICT onUpdate: RESTRICT referencedColumnNames: id referencedTableName: t_many_to_one_child validate: true ================================================ FILE: src/test/resources/jdl/prune-expected.jdl ================================================ entity Demo { } ================================================ FILE: src/test/resources/jdl/prune-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: prune-1 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: autoIncrement: true constraints: nullable: false primaryKey: true name: id type: BIGINT tableName: class - changeSet: id: prune-2 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: autoIncrement: true constraints: nullable: false primaryKey: true name: id type: BIGINT tableName: demo - changeSet: id: prune-3 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: autoIncrement: true constraints: nullable: false primaryKey: true name: id type: BIGINT tableName: student_detail ================================================ FILE: src/test/resources/jdl/readonly-expected-sqlserver.jdl ================================================ /** view */ @readOnly entity Nonupdatable { id Long required, num Integer required } entity TestTable { num Integer required } /** view */ @readOnly entity Updatable { id Long required, num Integer required } ================================================ FILE: src/test/resources/jdl/readonly-expected.jdl ================================================ /** view */ @readOnly entity Nonupdatable { id Long required, num Integer required } entity TestTable { num Integer required } /** view */ entity Updatable { id Long required, num Integer required } ================================================ FILE: src/test/resources/jdl/readonly-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: view-create-table author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false name: num type: int tableName: test_table - changeSet: id: view-create-view-updateable author: jason.long changes: - createView: fullDefinition: false selectQuery: select t.* from test_table t viewName: updatable - changeSet: id: view-create-view-nonupdateable author: jason.long dbms: '!oracle' changes: - createView: fullDefinition: false selectQuery: select distinct t.* from test_table t viewName: nonupdatable - changeSet: id: view-create-view-nonupdateable-oracle author: jason.long dbms: oracle changes: - createView: fullDefinition: false selectQuery: select distinct t.* from test_table t with read only viewName: nonupdatable ================================================ FILE: src/test/resources/jdl/reflexive_relationship-expected.jdl ================================================ entity Invoice { num String required unique maxlength(10), amount BigDecimal required } // Relations relationship OneToOne { Invoice{parent(num)} to Invoice{invoice(num)} } ================================================ FILE: src/test/resources/jdl/reflexive_relationship-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: create_table_invoices-1 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false unique: true name: num type: varchar(10) - column: constraints: nullable: false name: amount type: DECIMAL(19, 5) - column: constraints: nullable: false unique: true name: parent_id type: bigint tableName: invoice - changeSet: id: add_reflexive_foreign_key-3 author: jason.long changes: - addForeignKeyConstraint: baseColumnNames: parent_id baseTableName: invoice constraintName: fk_invoice_parent referencedColumnNames: id referencedTableName: invoice validate: true ================================================ FILE: src/test/resources/jdl/table_name-expected.jdl ================================================ entity T(t) { f Integer } ================================================ FILE: src/test/resources/jdl/table_name-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: table_name-1 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: name: f type: int tableName: t ================================================ FILE: src/test/resources/jdl/tables-only-expected.jdl ================================================ entity TestTable { num Integer required } ================================================ FILE: src/test/resources/jdl/tables-only-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: view-create-table author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: constraints: nullable: false name: num type: int tableName: test_table - changeSet: id: view-create-view-updateable author: jason.long changes: - createView: fullDefinition: false selectQuery: select t.* from test_table t viewName: updatable - changeSet: id: view-create-view-nonupdateable author: jason.long changes: - createView: fullDefinition: false selectQuery: select distinct t.* from test_table t viewName: nonupdatable ================================================ FILE: src/test/resources/jdl/undefined_error-expected.jdl ================================================ //Place hol ================================================ FILE: src/test/resources/jdl/undefined_error-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: undefined_unsupported-1 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: name: supported type: integer - column: name: unsupported type: integer[] tableName: undefined_unsupported ================================================ FILE: src/test/resources/jdl/undefined_skip-expected.jdl ================================================ entity UndefinedUnsupported { supported Integer } ================================================ FILE: src/test/resources/jdl/undefined_skip-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: undefined_unsupported-1 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: name: supported type: integer - column: name: unsupported type: integer[] tableName: undefined_unsupported ================================================ FILE: src/test/resources/jdl/undefined_unsupported-expected.jdl ================================================ entity UndefinedUnsupported { supported Integer, /** ARRAY */ unsupported Unsupported } ================================================ FILE: src/test/resources/jdl/undefined_unsupported-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: undefined_unsupported-1 author: jason.long changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: bigint - column: name: supported type: integer - column: name: unsupported type: integer[] tableName: undefined_unsupported ================================================ FILE: src/test/resources/jdl/unique-expected-oracle.jdl ================================================ entity Student { id Integer required, ssnnumber String required unique maxlength(100) } ================================================ FILE: src/test/resources/jdl/unique-expected-sqlserver.jdl ================================================ entity Student { id Integer required, ssnNumber String required unique maxlength(100) } ================================================ FILE: src/test/resources/jdl/unique-expected.jdl ================================================ entity Student { id Integer required, ssnNumber String required unique maxlength(100) } ================================================ FILE: src/test/resources/jdl/unique-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: unique-1 author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: autoIncrement: true constraints: nullable: false primaryKey: true name: id type: INT - column: constraints: nullable: false unique: true name: ssnNumber type: VARCHAR(100) tableName: student ================================================ FILE: src/test/resources/jdl/uuid_id_required-expected-mariadb.jdl ================================================ entity UuidIdRequired { id String required maxlength(36) } ================================================ FILE: src/test/resources/jdl/uuid_id_required-expected-mysql.jdl ================================================ entity UuidIdRequired { id String required maxlength(36) } ================================================ FILE: src/test/resources/jdl/uuid_id_required-expected-postgresql.jdl ================================================ entity UuidIdRequired { id UUID } ================================================ FILE: src/test/resources/jdl/uuid_id_required-expected-sqlserver.jdl ================================================ entity UuidIdRequired { id UUID } ================================================ FILE: src/test/resources/jdl/uuid_id_required-expected.jdl ================================================ entity UuidIdRequired { id UUID } ================================================ FILE: src/test/resources/jdl/uuid_id_required-liquibase-changeset-sqlserver.yaml ================================================ databaseChangeLog: - changeSet: id: uuid_id_required author: jason.long #dbms: sqlserver changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: uniqueidentifier tableName: uuid_id_required ================================================ FILE: src/test/resources/jdl/uuid_id_required-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: uuid_id_required author: jason.long #dbms: mysql,mariadb changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: id type: uuid tableName: uuid_id_required ================================================ FILE: src/test/resources/jdl/views-expected.jdl ================================================ entity OrderDetails { orderNumber String required maxlength(255), productCode String required maxlength(50), quantityOrdered Integer required, priceEach BigDecimal required } /** view */ @readOnly entity SalePerOrder { orderNumber String required maxlength(255), total BigDecimal } ================================================ FILE: src/test/resources/jdl/views-liquibase-changeset.yaml ================================================ databaseChangeLog: - changeSet: id: view-create-table author: Generated with liquibase:generate-changelog from flyway sql from existing tests changes: - createTable: columns: - column: constraints: nullable: false primaryKey: true name: order_number type: VARCHAR(255) - column: constraints: nullable: false primaryKey: true name: product_code type: VARCHAR(50) - column: constraints: nullable: false name: quantity_ordered type: INT - column: constraints: nullable: false name: price_each type: DECIMAL(19, 5) tableName: order_details - changeSet: id: view-create-view-all author: jason.long (generated) dbms: '!oracle' changes: - createView: fullDefinition: false selectQuery: select order_details.order_number AS order_number, sum((order_details.quantity_ordered * order_details.price_each)) AS total from order_details group by order_details.order_number viewName: sale_per_order - changeSet: id: view-create-view-oracle author: mhmmdd dbms: oracle changes: - createView: fullDefinition: false selectQuery: select order_details.order_number AS order_number, cast(sum((order_details.quantity_ordered * order_details.price_each)) as number(19,5)) as total from order_details group by order_details.order_number with read only viewName: sale_per_order ================================================ FILE: src/test/resources/liquibase.properties ================================================ #liquibase.properties liquibase.sql.logLevel=DEBUG liquibase-logLevel=DEBUG url=jdbc:mysql://localhost:3309/sql-to-jdl username=root password= driver=com.mysql.jdbc.Driver outputChangeLogFile=src/test/resources/liquibase-outputChangeLog.yaml