Skip to content

Commit 8347274

Browse files
committed
Adding logic to generate @mapsid mapping
1 parent 4284eb5 commit 8347274

File tree

8 files changed

+165
-69
lines changed

8 files changed

+165
-69
lines changed
Lines changed: 101 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.ngbsn.generator;
22

33
import org.apache.commons.text.CaseUtils;
4+
import org.apache.commons.text.WordUtils;
45
import org.ngbsn.model.Column;
6+
import org.ngbsn.model.EmbeddableClass;
57
import org.ngbsn.model.ForeignKeyConstraint;
68
import org.ngbsn.model.Table;
79
import org.ngbsn.model.annotations.fieldAnnotations.*;
@@ -10,15 +12,13 @@
1012
import java.util.List;
1113
import java.util.Optional;
1214
import java.util.Set;
15+
import java.util.stream.Collectors;
16+
import java.util.stream.Stream;
1317

1418
import static org.ngbsn.generator.ModelGenerator.tablesMap;
1519

1620
/**
17-
* Is there more than 1 foreign key? - This is a linked table
18-
* Are all fields foreign keys? @ManyToMany and no separate entity needed for linked table
19-
* Are there some non-foreign key fields? - @ManyToOne in a separate entity for linked table
20-
* <p>
21-
* Is there a primary key in the foreign keys list? Mark this as @MapsId
21+
* This class contains logic for generating all the association mappings
2222
*/
2323
public class AssociationMappingsGenerator {
2424

@@ -35,17 +35,17 @@ public static void generateMappings(final Table table) {
3535
//Case: There are multiple Foreign Keys or multiple Composite Foreign Keys
3636
//Treat this as a Link table
3737
List<String> foreignKeyColumns = foreignKeyConstraintList.stream().flatMap(foreignKeyConstraint -> foreignKeyConstraint.getColumns().stream()).toList();
38-
Optional<Column> optionalKey = table.getColumns().stream().filter(column -> !foreignKeyColumns.contains(column.getName())).findAny();
38+
Optional<Column> optionalKey = table.getColumns().stream().filter(column -> !foreignKeyColumns.contains(column.getColumnName())).findAny();
3939
if (optionalKey.isEmpty() && foreignKeyConstraintList.size() == 2) {
4040
//Case: All fields are foreign keys. Also, the relation exits between 2 entities only
4141
//Remove this link entity from the tablesMap as separate entity is not needed to track Link Table. Use @ManyToMany on other 2 entities
42-
tablesMap.remove(table.getName());
42+
tablesMap.remove(table.getTableName());
4343
Table table1 = tablesMap.get(foreignKeyConstraintList.get(0).getReferencedTableName());
4444
Table table2 = tablesMap.get(foreignKeyConstraintList.get(1).getReferencedTableName());
4545

4646
//Adding @ManyToMany and @JoinTable to table1
4747
Column column1 = new Column();
48-
column1.setFieldName(CaseUtils.toCamelCase(table2.getName(), false, '_'));
48+
column1.setFieldName(CaseUtils.toCamelCase(table2.getTableName(), false, '_'));
4949
column1.setType(table2.getClassName());
5050
column1.getAnnotations().add(ManyToManyAnnotation.builder().build().toString());
5151
Set<JoinColumnAnnotation> joinColumnAnnotations = new HashSet<>();
@@ -56,12 +56,12 @@ public static void generateMappings(final Table table) {
5656
for (String column : foreignKeyConstraintList.get(1).getColumns()) {
5757
joinInverseColumnAnnotations.add(JoinColumnAnnotation.builder().name(column).build());
5858
}
59-
column1.getAnnotations().add(JoinTableAnnotation.builder().tableName(table.getName()).joinColumns(joinColumnAnnotations).inverseJoinColumns(joinInverseColumnAnnotations).build().toString());
59+
column1.getAnnotations().add(JoinTableAnnotation.builder().tableName(table.getTableName()).joinColumns(joinColumnAnnotations).inverseJoinColumns(joinInverseColumnAnnotations).build().toString());
6060
table1.getColumns().add(column1);
6161

6262
//Adding @ManyToMany(mappedBy) to table2
6363
Column column2 = new Column();
64-
column2.setFieldName(CaseUtils.toCamelCase(table1.getName(), false, '_'));
64+
column2.setFieldName(CaseUtils.toCamelCase(table1.getTableName(), false, '_'));
6565
column2.setType(table1.getClassName());
6666
column2.getAnnotations().add(ManyToManyAnnotation.builder().mappedBy(column1.getFieldName()).build().toString());
6767
table2.getColumns().add(column2);
@@ -78,39 +78,107 @@ public static void generateMappings(final Table table) {
7878

7979
}
8080

81+
/**
82+
* This method will handle each foreign key constraint in the table.
83+
*
84+
* @param table table
85+
* @param foreignKeyConstraint foreignKeyConstraint
86+
*/
8187
private static void addBothUnidirectionalMappings(Table table, ForeignKeyConstraint foreignKeyConstraint) {
8288
Table referencedTable = tablesMap.get(foreignKeyConstraint.getReferencedTableName());
83-
Column foreignKeyColumn = new Column();
84-
foreignKeyColumn.setFieldName(CaseUtils.toCamelCase(referencedTable.getName(), false, '_'));
85-
foreignKeyColumn.setType(referencedTable.getClassName());
86-
foreignKeyColumn.getAnnotations().add(new ManyToOneAnnotation().toString());
87-
table.getColumns().add(foreignKeyColumn);
88-
89-
Column childKeyColumn = new Column();
90-
childKeyColumn.setFieldName(CaseUtils.toCamelCase(table.getName(), false, '_'));
91-
childKeyColumn.setType("Set<" + table.getClassName() + ">");
92-
childKeyColumn.getAnnotations().add(OneToManyAnnotation.builder().mappedBy(foreignKeyColumn.getFieldName()).build().toString());
93-
referencedTable.getColumns().add(childKeyColumn);
94-
89+
//In the Child table, create a new column having field name as Parent(Referenced) Table.
90+
Column parentTableField = new Column();
91+
parentTableField.setFieldName(CaseUtils.toCamelCase(referencedTable.getTableName(), false, '_'));
92+
parentTableField.setType(referencedTable.getClassName());
93+
parentTableField.getAnnotations().add(new ManyToOneAnnotation().toString());
94+
table.getColumns().add(parentTableField);
95+
96+
//In the Parent(Referenced) table, create a new column having field name as child Table.
97+
Column childTableField = new Column();
98+
childTableField.setFieldName(CaseUtils.toCamelCase(table.getTableName(), false, '_'));
99+
childTableField.setType("Set<" + table.getClassName() + ">");
100+
childTableField.getAnnotations().add(OneToManyAnnotation.builder().mappedBy(parentTableField.getFieldName()).build().toString());
101+
referencedTable.getColumns().add(childTableField);
102+
103+
//get EmbeddedId for this table
104+
Optional<EmbeddableClass> optionalEmbeddableId = table.getEmbeddableClasses().stream().filter(EmbeddableClass::isEmbeddedId).findFirst();
105+
EmbeddableClass embeddableId = optionalEmbeddableId.orElse(null);
106+
Set<Column> allPrimaryKeyColumns = getAllPrimaryKeys(table, embeddableId); //get all primary keys
107+
108+
//Case: Composite Foreign key
95109
if (foreignKeyConstraint.getColumns().size() > 1) {
96-
//Case: Composite Foreign key
110+
Set<Column> setOfForeignKeyColumns = setOfForeignKeys(table, foreignKeyConstraint);
111+
//If composite foreign key is inside the composite primary key, don't remove them from table.
112+
//This case assumes there is a primary composite key
113+
//Add a @MapsId annotation to the referenced table field
114+
if (embeddableId != null && allPrimaryKeyColumns.containsAll(setOfForeignKeyColumns)) {
115+
EmbeddableClass foreignCompositeKeyEmbedded = new EmbeddableClass(); //Create a new embeddable for this foreign composite key
116+
String embeddableName = setOfForeignKeyColumns.stream().map(Column::getFieldName).collect(Collectors.joining());
117+
foreignCompositeKeyEmbedded.setClassName(WordUtils.capitalize(embeddableName));
118+
foreignCompositeKeyEmbedded.setFieldName(embeddableName);
119+
table.getEmbeddableClasses().add(foreignCompositeKeyEmbedded); //add new embeddable to the Table list of Embeddables
120+
setOfForeignKeyColumns.forEach(column -> {
121+
//add the individual foreign keys columns the newly created embeddable
122+
foreignCompositeKeyEmbedded.getColumns().add(column);
123+
//Remove the individual foreign keys from EmbeddedId and add the newly created embeddable into EmbeddedId
124+
embeddableId.getColumns().remove(column);
125+
});
126+
Column foreignCompositeField = new Column();
127+
foreignCompositeField.setType(foreignCompositeKeyEmbedded.getClassName());
128+
foreignCompositeField.setFieldName(foreignCompositeKeyEmbedded.getFieldName());
129+
embeddableId.getColumns().add(foreignCompositeField);
130+
131+
if(embeddableId.getFieldName() != null)
132+
parentTableField.getAnnotations().add(MapsIdAnnotation.builder().fieldName(foreignCompositeField.getFieldName()).build().toString());
133+
} else {
134+
//There is no primary Composite key
135+
//If composite foreign key is not inside the composite primary key, then remove it from the table
136+
setOfForeignKeyColumns.forEach(column -> table.getColumns().remove(column));
137+
}
138+
97139
Set<JoinColumnAnnotation> joinColumns = new HashSet<>();
140+
//Create the @JoinColumn annotations for the parentTableField
98141
for (int i = 0; i < foreignKeyConstraint.getColumns().size(); i++) {
99-
int finalI = i;
100-
Optional<Column> optionalColumn = table.getColumns().stream().filter(column -> column.getName().equals(foreignKeyConstraint.getColumns().get(finalI))).findFirst();
101-
optionalColumn.ifPresent(column -> table.getColumns().remove(column));
102-
103142
JoinColumnAnnotation joinColumnAnnotation = JoinColumnAnnotation.builder().name(foreignKeyConstraint.getColumns().get(i)).referencedColumnName(foreignKeyConstraint.getReferencedColumns().get(i)).build();
104143
joinColumns.add(joinColumnAnnotation);
144+
}
145+
parentTableField.getAnnotations().add(JoinColumnsAnnotation.builder().joinColumns(joinColumns).build().toString());
105146

147+
}
148+
//Case: Single Foreign Key
149+
else {
150+
//Get the foreign key column from the table
151+
Optional<Column> optionalColumn = table.getColumns().stream().filter(column -> column.getColumnName() != null && column.getColumnName().equals(foreignKeyConstraint.getReferencedColumns().get(0))).findFirst();
152+
if (optionalColumn.isPresent()) {
153+
Column foreignKeyColumn = optionalColumn.get();
154+
//Check if foreign key is also a primary key, by iterating through the primary key list
155+
Optional<Column> optionalColumnPrimaryForeign = allPrimaryKeyColumns.stream().filter(column -> column.getColumnName() != null && column.getColumnName().equals(foreignKeyColumn.getColumnName())).findFirst();
156+
optionalColumnPrimaryForeign.ifPresentOrElse(column -> {
157+
//If foreign key is a primary key, don't remove it from table.
158+
//Add a @MapsId annotation to the referenced table field
159+
parentTableField.getAnnotations().add(MapsIdAnnotation.builder().fieldName(column.getFieldName()).build().toString());
160+
}, () -> {
161+
//If foreign key is not a primary key, then remove it from the table
162+
optionalColumn.ifPresent(column -> table.getColumns().remove(column));
163+
});
106164
}
107-
foreignKeyColumn.getAnnotations().add(JoinColumnsAnnotation.builder().joinColumns(joinColumns).build().toString());
165+
//Add a @JoinColumn annotation for the referenced table field
166+
parentTableField.getAnnotations().add(JoinColumnAnnotation.builder().name(foreignKeyConstraint.getReferencedColumns().get(0)).referencedColumnName(foreignKeyConstraint.getReferencedColumns().get(0)).build().toString());
167+
}
168+
}
108169

109-
} else {
110-
//Case: Single Foreign Key
111-
Optional<Column> optionalColumn = table.getColumns().stream().filter(column -> column.getName().equals(foreignKeyConstraint.getReferencedColumns().get(0))).findFirst();
112-
optionalColumn.ifPresent(column -> table.getColumns().remove(column));
113-
foreignKeyColumn.getAnnotations().add(JoinColumnAnnotation.builder().name(CaseUtils.toCamelCase(foreignKeyConstraint.getReferencedColumns().get(0), false, '_')).referencedColumnName(foreignKeyConstraint.getReferencedColumns().get(0)).build().toString());
170+
private static Set<Column> setOfForeignKeys(Table table, ForeignKeyConstraint foreignKeyConstraint) {
171+
Stream<Column> allTableForeignKeyColumns = table.getColumns().stream();
172+
Stream<Column> allEmbeddedForeignKeyColumns = table.getEmbeddableClasses().stream().flatMap(embeddableClass -> embeddableClass.getColumns().stream());
173+
return Stream.concat(allTableForeignKeyColumns, allEmbeddedForeignKeyColumns).filter(column -> foreignKeyConstraint.getColumns().stream().anyMatch(s -> s.equals(column.getColumnName()))).collect(Collectors.toSet());
174+
}
175+
176+
private static Set<Column> getAllPrimaryKeys(Table table, EmbeddableClass embeddableId) {
177+
//Get set of primary Keys
178+
if(embeddableId != null){
179+
return embeddableId.getColumns().stream().filter(Column::isPrimaryKey).collect(Collectors.toSet());
180+
}else{
181+
return table.getColumns().stream().filter(Column::isPrimaryKey).collect(Collectors.toSet());
114182
}
115183
}
116184
}

src/main/java/org/ngbsn/generator/ModelGenerator.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.slf4j.LoggerFactory;
2020

2121
import java.util.*;
22+
import java.util.stream.Collectors;
2223

2324
import static org.ngbsn.generator.AssociationMappingsGenerator.generateMappings;
2425

@@ -38,17 +39,17 @@ public static List<Table> parse(final String sqlScript) {
3839
if (statement instanceof CreateTable parsedTable) {
3940
Table table = new Table();
4041
tablesMap.put(parsedTable.getTable().getName(), table);
41-
table.setName(parsedTable.getTable().getName());
42-
table.setClassName(CaseUtils.toCamelCase(table.getName(), true, '_'));
42+
table.setTableName(parsedTable.getTable().getName());
43+
table.setClassName(CaseUtils.toCamelCase(table.getTableName(), true, '_'));
4344

44-
List<String> tableAnnotations = new ArrayList<>();
45+
Set<String> tableAnnotations = new HashSet<>();
4546
table.setAnnotations(tableAnnotations);
4647
//Adding @Entity
4748
tableAnnotations.add(new EntityAnnotation().toString());
4849
//Adding @Table
4950
tableAnnotations.add(TableAnnotation.builder().tableName(parsedTable.getTable().getName()).build().toString());
5051

51-
List<Column> columns = new ArrayList<>();
52+
Set<Column> columns = new HashSet<>();
5253
table.setColumns(columns);
5354
extractColumns(parsedTable, columns);
5455

@@ -98,17 +99,24 @@ private static void extractPrimaryKeys(CreateTable parsedTable, Table table) {
9899
Optional<Index> optionalIndex = parsedTable.getIndexes().stream().filter(index -> index.getType().equals("PRIMARY KEY")).findFirst();
99100
List<Index.ColumnParams> columnParamsList = optionalIndex.map(Index::getColumns).orElse(null);
100101
if (columnParamsList != null) {
102+
Set<Column> primaryKeyColumns = table.getColumns().stream().
103+
filter(column -> columnParamsList.stream().anyMatch(columnParams -> columnParams.getColumnName().equals(column.getColumnName()))).collect(Collectors.toSet());
104+
101105
if (columnParamsList.size() > 1) {
102106
table.setNumOfPrimaryKeyColumns(columnParamsList.size());
103-
EmbeddableClass embeddableClass = EmbeddableClass
104-
.builder()
105-
.className(table.getClassName() + "PK")
106-
.fieldName(CaseUtils.toCamelCase(table.getName(), false, '_') + "PK")
107-
.build();
108-
table.setEmbeddableClass(embeddableClass);
107+
//create a embeddedId within Table
108+
EmbeddableClass embeddedId = new EmbeddableClass();
109+
embeddedId.setClassName(table.getClassName() + "PK");
110+
embeddedId.setFieldName(CaseUtils.toCamelCase(table.getTableName(), false, '_') + "PK");
111+
embeddedId.setEmbeddedId(true);
112+
table.getEmbeddableClasses().add(embeddedId);
113+
114+
//remove the primary keys columns from table and add inside EmbeddedId
115+
primaryKeyColumns.forEach(column -> {
116+
table.getColumns().remove(column);
117+
embeddedId.getColumns().add(column);
118+
});
109119
}
110-
List<Column> primaryKeyColumns = table.getColumns().stream().
111-
filter(column -> columnParamsList.stream().anyMatch(columnParams -> columnParams.getColumnName().equals(column.getName()))).toList();
112120
primaryKeyColumns.forEach(column -> column.setPrimaryKey(true));
113121
}
114122
}
@@ -119,15 +127,15 @@ private static void extractPrimaryKeys(CreateTable parsedTable, Table table) {
119127
* @param parsedTable Parsed table
120128
* @param columns List of generated column models
121129
*/
122-
private static void extractColumns(CreateTable parsedTable, List<Column> columns) {
130+
private static void extractColumns(CreateTable parsedTable, Set<Column> columns) {
123131
parsedTable.getColumnDefinitions().forEach(columnDefinition -> {
124132
Column column = new Column();
125133
columns.add(column);
126-
List<String> columnAnnotations = new ArrayList<>();
134+
Set<String> columnAnnotations = new HashSet<>();
127135
column.setAnnotations(columnAnnotations);
128-
column.setName(columnDefinition.getColumnName());
136+
column.setColumnName(columnDefinition.getColumnName());
129137
//Adding @Column
130-
columnAnnotations.add(ColumnAnnotation.builder().columnName(column.getName()).build().toString());
138+
columnAnnotations.add(ColumnAnnotation.builder().columnName(column.getColumnName()).build().toString());
131139
column.setFieldName(CaseUtils.toCamelCase(columnDefinition.getColumnName(), false, '_'));
132140
column.setType(SQLToJavaMapping.sqlToJavaMap.get(columnDefinition.getColDataType().getDataType()));
133141

src/main/java/org/ngbsn/model/Column.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
import lombok.Getter;
44
import lombok.Setter;
55

6-
import java.util.ArrayList;
7-
import java.util.List;
6+
import java.util.HashSet;
7+
import java.util.Set;
88

99
@Setter
1010
@Getter
1111
public class Column {
1212

13-
private String name;
13+
private String columnName;
1414
private String fieldName;
1515
private String type;
1616
private boolean primaryKey;
17-
private List<String> annotations = new ArrayList<>();;
17+
private Set<String> annotations = new HashSet<>();
1818
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package org.ngbsn.model;
22

3-
import lombok.Builder;
43
import lombok.Getter;
54
import lombok.Setter;
65

7-
@Builder
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
89
@Setter
910
@Getter
1011
public class EmbeddableClass {
11-
private String className;
1212
private String fieldName;
13+
private String className;
14+
private Set<Column> columns = new HashSet<>();
15+
private boolean embeddedId;
1316
}

src/main/java/org/ngbsn/model/Table.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44
import lombok.Setter;
55

66
import java.util.ArrayList;
7+
import java.util.HashSet;
78
import java.util.List;
9+
import java.util.Set;
810

911

1012
@Setter
1113
@Getter
1214
public class Table {
1315

14-
private String name;
16+
private String tableName;
1517
private String className;
16-
private List<Column> columns = new ArrayList<>();
17-
private List<String> annotations = new ArrayList<>();
18+
private Set<Column> columns = new HashSet<>();
19+
private Set<String> annotations = new HashSet<>();
1820
private int numOfPrimaryKeyColumns;
1921
private List<ForeignKeyConstraint> foreignKeyConstraints = new ArrayList<>();
20-
private EmbeddableClass embeddableClass;
21-
22+
private Set<EmbeddableClass> embeddableClasses = new HashSet<>();
2223
}

0 commit comments

Comments
 (0)