Skip to content

Commit ad7b087

Browse files
authored
Initial Data Model for Schema SQL Generation (#1481)
Initial Data Model for Schema SQL Generation that can generate SQL for a simple entity. Closes #1478
1 parent a242153 commit ad7b087

File tree

9 files changed

+487
-2
lines changed

9 files changed

+487
-2
lines changed

spring-data-jdbc/pom.xml

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
<artifactId>spring-data-jdbc</artifactId>
99
<version>3.1.0-756-SNAPSHOT</version>
10-
1110
<name>Spring Data JDBC</name>
1211
<description>Spring Data module for JDBC repositories.</description>
1312
<url>https://projects.spring.io/spring-data-jdbc</url>

spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.springframework.data.util.TypeInformation;
2323
import org.springframework.util.Assert;
2424

25+
import java.util.Iterator;
26+
2527
/**
2628
* {@link MappingContext} implementation.
2729
*
@@ -101,5 +103,4 @@ protected RelationalPersistentProperty createPersistentProperty(Property propert
101103
public NamingStrategy getNamingStrategy() {
102104
return this.namingStrategy;
103105
}
104-
105106
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.relational.core.mapping.schemasqlgeneration;
17+
18+
import java.util.HashMap;
19+
20+
public class BaseTypeMapper {
21+
22+
final HashMap<Class<?>,String> mapClassToDatabaseType = new HashMap<Class<?>,String>();
23+
24+
public BaseTypeMapper() {
25+
mapClassToDatabaseType.put(String.class, "VARCHAR(255)");
26+
mapClassToDatabaseType.put(Boolean.class, "TINYINT");
27+
mapClassToDatabaseType.put(Double.class, "DOUBLE");
28+
mapClassToDatabaseType.put(Float.class, "FLOAT");
29+
mapClassToDatabaseType.put(Integer.class, "INT");
30+
}
31+
public String databaseTypeFromClass(Class<?> type) {
32+
return mapClassToDatabaseType.get(type);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.relational.core.mapping.schemasqlgeneration;
17+
18+
import org.springframework.data.relational.core.sql.SqlIdentifier;
19+
20+
import java.io.Serial;
21+
import java.io.Serializable;
22+
23+
/**
24+
* Class that models a Column for generating SQL for Schema generation.
25+
*
26+
* @author Kurt Niemi
27+
*/
28+
public class ColumnModel implements Serializable {
29+
@Serial
30+
private static final long serialVersionUID = 1L;
31+
private final SqlIdentifier name;
32+
private final String type;
33+
private final boolean nullable;
34+
35+
public ColumnModel(SqlIdentifier name, String type, boolean nullable) {
36+
this.name = name;
37+
this.type = type;
38+
this.nullable = nullable;
39+
}
40+
41+
public ColumnModel(SqlIdentifier name, String type) {
42+
this.name = name;
43+
this.type = type;
44+
this.nullable = false;
45+
}
46+
47+
public SqlIdentifier getName() {
48+
return name;
49+
}
50+
51+
public String getType() {
52+
return type;
53+
}
54+
55+
public boolean isNullable() {
56+
return nullable;
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.relational.core.mapping.schemasqlgeneration;
17+
18+
import java.io.Serial;
19+
import java.io.Serializable;
20+
21+
/**
22+
* Class that models a Foreign Key relationship for generating SQL for Schema generation.
23+
*
24+
* @author Kurt Niemi
25+
*/
26+
public class ForeignKeyColumnModel implements Serializable {
27+
@Serial
28+
private static final long serialVersionUID = 1L;
29+
private final TableModel foreignTable;
30+
private final ColumnModel foreignColumn;
31+
private final ColumnModel column;
32+
33+
public ForeignKeyColumnModel(TableModel foreignTable, ColumnModel foreignColumn, ColumnModel column) {
34+
this.foreignTable = foreignTable;
35+
this.foreignColumn = foreignColumn;
36+
this.column = column;
37+
}
38+
39+
public TableModel getForeignTable() {
40+
return foreignTable;
41+
}
42+
43+
public ColumnModel getForeignColumn() {
44+
return foreignColumn;
45+
}
46+
47+
public ColumnModel getColumn() {
48+
return column;
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.relational.core.mapping.schemasqlgeneration;
17+
18+
import org.springframework.data.relational.core.mapping.BasicRelationalPersistentProperty;
19+
import org.springframework.data.relational.core.mapping.Column;
20+
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
21+
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
22+
23+
import java.io.Serial;
24+
import java.io.Serializable;
25+
import java.util.ArrayList;
26+
import java.util.HashMap;
27+
import java.util.Iterator;
28+
import java.util.List;
29+
30+
/**
31+
* Model class that contains Table/Column information that can be used
32+
* to generate SQL for Schema generation.
33+
*
34+
* @author Kurt Niemi
35+
*/
36+
public class SchemaSQLGenerationDataModel implements Serializable {
37+
@Serial
38+
private static final long serialVersionUID = 1L;
39+
private final List<TableModel> tableData = new ArrayList<TableModel>();
40+
BaseTypeMapper typeMapper;
41+
42+
/**
43+
* Default constructor so that we can deserialize a model
44+
*/
45+
public SchemaSQLGenerationDataModel() {
46+
}
47+
48+
/**
49+
* Create model from a RelationalMappingContext
50+
*/
51+
public SchemaSQLGenerationDataModel(RelationalMappingContext context) {
52+
53+
if (typeMapper == null) {
54+
typeMapper = new BaseTypeMapper();
55+
}
56+
57+
for (RelationalPersistentEntity entity : context.getPersistentEntities()) {
58+
TableModel tableModel = new TableModel(entity.getTableName());
59+
60+
Iterator<BasicRelationalPersistentProperty> iter =
61+
entity.getPersistentProperties(Column.class).iterator();
62+
63+
while (iter.hasNext()) {
64+
BasicRelationalPersistentProperty p = iter.next();
65+
ColumnModel columnModel = new ColumnModel(p.getColumnName(),
66+
typeMapper.databaseTypeFromClass(p.getActualType()),
67+
true);
68+
tableModel.getColumns().add(columnModel);
69+
}
70+
tableData.add(tableModel);
71+
}
72+
}
73+
74+
public List<TableModel> getTableData() {
75+
return tableData;
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.relational.core.mapping.schemasqlgeneration;
17+
18+
import org.springframework.data.relational.core.sql.IdentifierProcessing;
19+
20+
import java.util.ArrayList;
21+
import java.util.HashMap;
22+
import java.util.List;
23+
24+
public class SchemaSQLGenerator {
25+
26+
private final IdentifierProcessing identifierProcssing;
27+
public SchemaSQLGenerator(IdentifierProcessing identifierProcessing) {
28+
this.identifierProcssing = identifierProcessing;
29+
}
30+
31+
List<List<TableModel>> reorderTablesInHierarchy(SchemaSQLGenerationDataModel dataModel) {
32+
33+
// ::TODO:: Take Parent/Child relationships into account (i.e if a child table has
34+
// a Foreign Key to a table, that parent table needs to be created first.
35+
36+
// For now this method will simple put the tables in the same level
37+
List<List<TableModel>> orderedTables = new ArrayList<List<TableModel>>();
38+
List<TableModel> tables = new ArrayList<TableModel>();
39+
40+
for (TableModel table : dataModel.getTableData()) {
41+
tables.add(table);
42+
}
43+
orderedTables.add(tables);
44+
45+
return orderedTables;
46+
}
47+
48+
HashMap<Class<?>,String> mapClassToSQLType = null;
49+
50+
public String generateSQL(ColumnModel column) {
51+
52+
StringBuilder sql = new StringBuilder();
53+
sql.append(column.getName().toSql(identifierProcssing));
54+
sql.append(" ");
55+
56+
sql.append(column.getType());
57+
58+
if (!column.isNullable()) {
59+
sql.append(" NOT NULL");
60+
}
61+
62+
return sql.toString();
63+
}
64+
65+
public String generatePrimaryKeySQL(TableModel table) {
66+
// ::TODO:: Implement
67+
return "";
68+
}
69+
70+
public String generateForeignKeySQL(TableModel table) {
71+
// ::TODO:: Implement
72+
return "";
73+
}
74+
75+
public String generateSQL(TableModel table) {
76+
77+
StringBuilder sql = new StringBuilder();
78+
79+
sql.append("CREATE TABLE ");
80+
sql.append(table.getName().toSql(identifierProcssing));
81+
sql.append(" (");
82+
83+
int numColumns = table.getColumns().size();
84+
for (int i=0; i < numColumns; i++) {
85+
sql.append(generateSQL(table.getColumns().get(i)));
86+
if (i != numColumns-1) {
87+
sql.append(",");
88+
}
89+
}
90+
91+
sql.append(generatePrimaryKeySQL(table));
92+
sql.append(generateForeignKeySQL(table));
93+
94+
sql.append(" );");
95+
96+
return sql.toString();
97+
}
98+
99+
public String generateSQL(SchemaSQLGenerationDataModel dataModel) {
100+
101+
StringBuilder sql = new StringBuilder();
102+
List<List<TableModel>> orderedTables = reorderTablesInHierarchy(dataModel);
103+
104+
for (List<TableModel> tables : orderedTables) {
105+
for (TableModel table : tables) {
106+
String tableSQL = generateSQL(table);
107+
sql.append(tableSQL + "\n");
108+
}
109+
}
110+
111+
return sql.toString();
112+
}
113+
}

0 commit comments

Comments
 (0)