Skip to content

Commit 8597f57

Browse files
committed
Changes:
1. Parsing individual statements using Jsql parser 2. Fix issues related to field names in OneToMany mappings 3. Remove @GeneratedValue for shared primary Keys 4. Support for Enum type and UUID 5. Removing the DEFAULT constraint from handling it.
1 parent 99421eb commit 8597f57

File tree

18 files changed

+3447
-89
lines changed

18 files changed

+3447
-89
lines changed

.github/workflows/release-workflow.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
name: release-workflow
22
run-name: Release run ${{ github.run_number }}
3-
on:
3+
on:
44
workflow_dispatch: # Only run when manually started
55
jobs:
66
release:
7-
name: Release
8-
runs-on: ubuntu-22.04
7+
name: Release
8+
runs-on: ubuntu-22.04
99
steps:
1010
- name: Checkout
1111
uses: actions/checkout@v3
@@ -22,8 +22,8 @@ jobs:
2222
check-latest: true
2323
server-id: 'ossrh'
2424
server-username: OSSRH_USERNAME
25-
server-password: OSSRH_PASSWORD
26-
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
25+
server-password: OSSRH_PASSWORD
26+
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
2727
gpg-passphrase: SIGN_KEY_PASS
2828
cache: 'maven'
2929
- name: Build & Deploy

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# sqlscript2jpa-codegen
22

3-
A Java library to generate Lombok wired JPA entities from DDL statements. The library offers both a maven plugin and a jar to be run as a standalone tool. It internally uses JSqlParser to parse the DDL statements.
3+
A Java library to generate Lombok wired JPA entities from DDL statements. The library offers both a maven plugin and a
4+
jar to be run as a standalone tool. It internally uses JSqlParser to parse the DDL statements.
45

56
### Getting Started
67

78
## Maven
89

9-
**sqlscript2jpa-codegen** is available at [Maven Central Repository](https://central.sonatype.com/artifact/io.github.ngbsn/sqlscript2jpa-codegen-maven-plugin).
10+
**sqlscript2jpa-codegen** is available
11+
at [Maven Central Repository](https://central.sonatype.com/artifact/io.github.ngbsn/sqlscript2jpa-codegen-maven-plugin).
1012
To use it, simply declare the following plugin in your pom file:
1113

1214
```xml
@@ -29,6 +31,7 @@ To use it, simply declare the following plugin in your pom file:
2931
</executions>
3032
</plugin>
3133
```
34+
3235
**sqlFilePath**: Path to the SQL file containing the DDL statements.
3336

3437
**packageName**: The package name for the generated entities.
@@ -37,9 +40,12 @@ By default, the source code will be generated under `target/generated-sources/sq
3740

3841
## Standalone
3942

40-
Get the jar from https://repo1.maven.org/maven2/io/github/ngbsn/sqlscript2jpa-codegen-maven-plugin/1.0.4/sqlscript2jpa-codegen-maven-plugin-1.0.4-standalone.jar
43+
Get the jar
44+
from https://repo1.maven.org/maven2/io/github/ngbsn/sqlscript2jpa-codegen-maven-plugin/1.0.4/sqlscript2jpa-codegen-maven-plugin-1.0.4-standalone.jar
4145

4246
```
4347
java -jar sqlscript2jpa-codegen-maven-plugin-1.0.4-standalone.jar "<sql_file_path>" "<package_name>"
4448
```
45-
This will generate the JPA entities in a folder structure as defined by the package name under `./target/generated-sources/sqlscript2jpa`
49+
50+
This will generate the JPA entities in a folder structure as defined by the package name
51+
under `./target/generated-sources/sqlscript2jpa`

pom.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
34

45
<modelVersion>4.0.0</modelVersion>
56
<groupId>io.github.ngbsn</groupId>
@@ -31,8 +32,8 @@
3132
<connection>scm:git:https://github.com/ngbsn/sqlscript2jpa-codegen.git</connection>
3233
<developerConnection>scm:git:https://github.com/ngbsn/sqlscript2jpa-codegen.git</developerConnection>
3334
<url>https://github.com/ngbsn/sqlscript2jpa-codegen</url>
34-
<tag>HEAD</tag>
35-
</scm>
35+
<tag>HEAD</tag>
36+
</scm>
3637

3738

3839
<properties>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.github.ngbsn.exception;
2+
3+
public class SQLParsingException extends Exception {
4+
public SQLParsingException(String message) {
5+
super(message);
6+
}
7+
}

src/main/java/io/github/ngbsn/generator/BiDirectionalMappingsGenerator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ private BiDirectionalMappingsGenerator() {
2222

2323
/**
2424
* Generate BiDirectional Mappings (many-to-many) for a specific table
25-
* @param table The table to be processed
25+
*
26+
* @param table The table to be processed
2627
* @param foreignKeyConstraintList List of generated foreignKeyConstraintList models
2728
*/
2829
static void addBiDirectionalMappings(Table table, List<ForeignKeyConstraint> foreignKeyConstraintList) {

src/main/java/io/github/ngbsn/generator/JPACodeGenerator.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import freemarker.template.Template;
66
import freemarker.template.TemplateException;
77
import freemarker.template.TemplateExceptionHandler;
8+
import io.github.ngbsn.exception.SQLParsingException;
89
import io.github.ngbsn.model.Table;
910
import lombok.extern.slf4j.Slf4j;
1011
import org.slf4j.Logger;
@@ -23,44 +24,46 @@
2324
public class JPACodeGenerator {
2425
private static final Logger logger = LoggerFactory.getLogger(JPACodeGenerator.class);
2526

26-
public static void main(final String[] args) throws TemplateException, IOException {
27+
public static void main(final String[] args) throws SQLParsingException, TemplateException, IOException {
2728
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(args[0]), StandardCharsets.UTF_8))) {
2829
String sqlScript = bufferedReader
2930
.lines()
3031
.collect(Collectors.joining("\n"));
3132
generateCode(sqlScript, args[1]);
3233
} catch (Exception e) {
3334
log.error("Error occurred while running the tool", e);
35+
throw e;
3436
}
3537
}
3638

3739
/**
38-
*
39-
* @param sqlScript The input DDL commands used for generating the models
40+
* @param sqlScript The input DDL commands used for generating the models
4041
* @param packageName package name for the entities sources to be generated
41-
* @throws IOException Thrown if template couldn't be read
42+
* @throws IOException Thrown if template couldn't be read
4243
* @throws TemplateException Thrown if template couldn't be processed
4344
*/
44-
public static void generateCode(final String sqlScript, final String packageName) throws IOException, TemplateException {
45+
public static void generateCode(final String sqlScript, final String packageName) throws IOException, TemplateException, SQLParsingException {
4546
logger.debug("sql script {}", sqlScript);
4647
List<Table> tables = generateModels(sqlScript);
4748
processTemplate(tables, packageName);
4849
}
4950

5051
/**
5152
* Generate the models needed to generate the sources
53+
*
5254
* @param sqlScript The input DDL commands used for generating the models
5355
* @return List of Table models
5456
*/
55-
private static List<Table> generateModels(final String sqlScript) {
57+
private static List<Table> generateModels(final String sqlScript) throws SQLParsingException {
5658
return ModelGenerator.parse(sqlScript);
5759
}
5860

5961
/**
6062
* This method processes the Apache FreeMarker entity template using the generated models
61-
* @param tables Generated models
63+
*
64+
* @param tables Generated models
6265
* @param packageName package name for the entities sources to be generated
63-
* @throws IOException Thrown if template couldn't be read
66+
* @throws IOException Thrown if template couldn't be read
6467
* @throws TemplateException Thrown if template cannot be processed
6568
*/
6669
private static void processTemplate(final List<Table> tables, final String packageName) throws IOException, TemplateException {

src/main/java/io/github/ngbsn/generator/ModelGenerator.java

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.ngbsn.generator;
22

3+
import io.github.ngbsn.exception.SQLParsingException;
34
import io.github.ngbsn.model.*;
45
import io.github.ngbsn.model.annotations.entity.EntityAnnotation;
56
import io.github.ngbsn.model.annotations.entity.TableAnnotation;
@@ -9,8 +10,9 @@
910
import io.github.ngbsn.util.SQLTypeToJpaTypeMapping;
1011
import io.github.ngbsn.util.Util;
1112
import jakarta.persistence.EnumType;
13+
import net.sf.jsqlparser.JSQLParserException;
1214
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
13-
import net.sf.jsqlparser.statement.Statements;
15+
import net.sf.jsqlparser.statement.Statement;
1416
import net.sf.jsqlparser.statement.alter.Alter;
1517
import net.sf.jsqlparser.statement.create.table.CreateTable;
1618
import net.sf.jsqlparser.statement.create.table.ForeignKeyIndex;
@@ -19,7 +21,10 @@
1921
import org.slf4j.Logger;
2022
import org.slf4j.LoggerFactory;
2123

24+
import javax.lang.model.SourceVersion;
2225
import java.util.*;
26+
import java.util.regex.Matcher;
27+
import java.util.regex.Pattern;
2328
import java.util.stream.Collectors;
2429

2530
/**
@@ -29,12 +34,10 @@
2934
public class ModelGenerator {
3035

3136
public static final String REGEX_ALL_QUOTES = "[\"']";
32-
33-
private ModelGenerator() {
34-
}
35-
3637
private static final Logger logger = LoggerFactory.getLogger(ModelGenerator.class);
3738
private static final Map<String, Table> tablesMap = new HashMap<>();
39+
private ModelGenerator() {
40+
}
3841

3942
/**
4043
* Clears the tables map. This is needed to have a clean start between JUnits
@@ -53,34 +56,55 @@ public static Map<String, Table> getTablesMap() {
5356

5457
/**
5558
* Parse the DDL statements using JSQL parser
59+
*
5660
* @param sqlScript The input DDL commands used for generating the models
5761
* @return List of Tables models
5862
*/
59-
static List<Table> parse(final String sqlScript) {
63+
static List<Table> parse(final String sqlScript) throws SQLParsingException {
64+
String extractedStatementWithoutDefaultConstraint = null;
6065
try {
61-
Statements statements = CCJSqlParserUtil.parseStatements(sqlScript);
66+
String[] extractedStatementsArray = sqlScript.split(";");
67+
List<String> extractedStatementsList = Arrays.stream(extractedStatementsArray).filter(s -> {
68+
String regexCreateTable = "CREATE\\s+TABLE";
69+
Pattern patternCreateTable = Pattern.compile(regexCreateTable);
70+
Matcher matcherCreateTable = patternCreateTable.matcher(s);
71+
72+
String regexAlterTable = "ALTER\\s+TABLE";
73+
Pattern patternAlterTable = Pattern.compile(regexAlterTable);
74+
Matcher matcherAlterTable = patternAlterTable.matcher(s);
75+
return matcherCreateTable.find() || matcherAlterTable.find();
76+
}).toList();
77+
List<Statement> statements = new ArrayList<>();
78+
for (String extractedStatement : extractedStatementsList) {
79+
extractedStatementWithoutDefaultConstraint = Util.removeDefaultConstraint(extractedStatement);
80+
statements.add(CCJSqlParserUtil.parse(extractedStatementWithoutDefaultConstraint));
81+
}
6282
processCreateTableStatements(statements);
6383
processAlterTableStatements(statements);
6484
AssociationMappingsGenerator.generateMappings();
6585
return tablesMap.values().stream().toList();
66-
} catch (Exception e) {
67-
logger.error("Error occurred", e);
86+
} catch (JSQLParserException e) {
87+
logger.error("Error occurred {}", e.getMessage());
88+
logger.error("Statement having issue with parsing {}", extractedStatementWithoutDefaultConstraint);
89+
throw new SQLParsingException("Statement having issue with parsing");
6890
}
69-
return new ArrayList<>();
7091
}
7192

7293
/**
7394
* Iterate over all the JSQL Create Table statements and prepare the list of Table Model
95+
*
7496
* @param statements Set of JSQL statements
7597
*/
76-
private static void processCreateTableStatements(final Statements statements) {
77-
statements.getStatements().forEach(statement -> {
98+
private static void processCreateTableStatements(final List<Statement> statements) {
99+
statements.forEach(statement -> {
78100
//Iterating over all Tables
79101
if (statement instanceof CreateTable parsedTable) {
80102
Table table = new Table();
81103
table.setTableName(parsedTable.getTable().getName().replaceAll(REGEX_ALL_QUOTES, ""));
82104
tablesMap.put(table.getTableName(), table);
83-
table.setClassName(Util.convertSnakeCaseToCamelCase(table.getTableName(), true));
105+
String className = Util.convertSnakeCaseToCamelCase(table.getTableName(), true);
106+
className = SourceVersion.isKeyword(className) ? className + "Entity" : className;
107+
table.setClassName(className);
84108

85109
Set<String> tableAnnotations = new HashSet<>();
86110
table.setAnnotations(tableAnnotations);
@@ -95,23 +119,28 @@ private static void processCreateTableStatements(final Statements statements) {
95119
//extract columns
96120
extractColumns(table, parsedTable, columns);
97121

98-
//extract primary keys
99-
Optional<Index> optionalIndex = parsedTable.getIndexes().stream().filter(index -> index.getType().equals("PRIMARY KEY")).findFirst();
100-
extractPrimaryKeys(optionalIndex.orElse(null), table);
122+
if (parsedTable.getIndexes() != null) {
123+
//extract primary keys
124+
Optional<Index> optionalIndex = parsedTable.getIndexes().stream().filter(index -> index.getType() != null &&
125+
index.getType().equals("PRIMARY KEY")).findFirst();
126+
extractPrimaryKeys(optionalIndex.orElse(null), table);
127+
128+
//extract foreign keys
129+
List<Index> foreignKeyIndexes = parsedTable.getIndexes().stream().filter(ForeignKeyIndex.class::isInstance).toList();
130+
extractForeignKeys(foreignKeyIndexes, table);
131+
}
101132

102-
//extract foreign keys
103-
List<Index> foreignKeyIndexes = parsedTable.getIndexes().stream().filter(ForeignKeyIndex.class::isInstance).toList();
104-
extractForeignKeys(foreignKeyIndexes, table);
105133
}
106134
});
107135
}
108136

109137
/**
110138
* Iterate over all the JSQL Alter Table statements and prepare the list of Table Model
139+
*
111140
* @param statements Set of JSQL statements
112141
*/
113-
private static void processAlterTableStatements(final Statements statements) {
114-
statements.getStatements().forEach(statement -> {
142+
private static void processAlterTableStatements(final List<Statement> statements) {
143+
statements.forEach(statement -> {
115144
//Iterating over all Tables
116145
// Look for primary and foreign keys in ALTER TABLE constraints
117146
if (statement instanceof Alter alterTable) {
@@ -121,7 +150,7 @@ private static void processAlterTableStatements(final Statements statements) {
121150
if (alterExpression.getIndex() instanceof ForeignKeyIndex) {
122151
//case: ALTER TABLE FOREIGN KEY
123152
foreignKeyIndexes.add(alterExpression.getIndex());
124-
} else if (alterExpression.getIndex().getType().equals("PRIMARY KEY")) {
153+
} else if (alterExpression.getIndex() != null && alterExpression.getIndex().getType().equals("PRIMARY KEY")) {
125154
//case: ALTER TABLE PRIMARY KEY
126155
extractPrimaryKeys(alterExpression.getIndex(), table);
127156
}
@@ -134,6 +163,7 @@ private static void processAlterTableStatements(final Statements statements) {
134163

135164
/**
136165
* Looking for all JSQL foreign keys in this table and adding it to our model
166+
*
137167
* @param foreignKeyIndexes List of JSQL foreign key indexes
138168
* @param table Table model
139169
*/
@@ -157,6 +187,7 @@ private static void extractForeignKeys(final List<Index> foreignKeyIndexes, fina
157187

158188
/**
159189
* Extracting primary key information from the JSQL parsed data and setting them into the Table model
190+
*
160191
* @param primaryKeyIndex JSQL primaryKeyIndex
161192
* @param table Table model
162193
*/
@@ -187,6 +218,7 @@ private static void extractPrimaryKeys(final Index primaryKeyIndex, final Table
187218

188219
/**
189220
* Generate column models for the JSQL parsed table
221+
*
190222
* @param parsedTable JSQL CreateTable
191223
* @param columns Set of generated column models
192224
*/
@@ -199,8 +231,10 @@ private static void extractColumns(final Table table, final CreateTable parsedTa
199231
column.setColumnName(columnDefinition.getColumnName().replaceAll(REGEX_ALL_QUOTES, ""));
200232
//Adding @Column
201233
columnAnnotations.add(ColumnAnnotation.builder().columnName(column.getColumnName()).build().toString());
202-
column.setFieldName(Util.convertSnakeCaseToCamelCase(column.getColumnName(), false));
203-
if(columnDefinition.getColDataType().getDataType().equals("ENUM")){
234+
String fieldName = Util.convertSnakeCaseToCamelCase(column.getColumnName(), false);
235+
fieldName = SourceVersion.isKeyword(fieldName) ? fieldName + table.getClassName() : fieldName;
236+
column.setFieldName(fieldName);
237+
if (columnDefinition.getColDataType().getDataType().equals("ENUM")) {
204238
TableEnum tableEnum = new TableEnum();
205239
table.getTableEnums().add(tableEnum);
206240
tableEnum.setEnumName(WordUtils.capitalize(column.getColumnName()) + "Enum");
@@ -210,8 +244,9 @@ private static void extractColumns(final Table table, final CreateTable parsedTa
210244
}
211245
column.setType(tableEnum.getEnumName());
212246
columnAnnotations.add(EnumeratedAnnotation.builder().value(EnumType.STRING).build().toString());
213-
}else{
214-
column.setType(SQLTypeToJpaTypeMapping.getTypeMapping(columnDefinition.getColDataType().getDataType()));
247+
} else {
248+
String mappedJavaType = SQLTypeToJpaTypeMapping.getTypeMapping(columnDefinition.getColDataType().getDataType());
249+
column.setType(Objects.requireNonNullElse(mappedJavaType, "Object"));
215250
}
216251

217252
//Check for NOT NULL

0 commit comments

Comments
 (0)