Skip to content

Commit 5342e02

Browse files
committed
Polishing.
Removed tests for circular references since they are not supported by Spring Data Relational. Closes #1599 See #756, #1600 Original pull request #1629
1 parent 9136d86 commit 5342e02

File tree

10 files changed

+115
-176
lines changed

10 files changed

+115
-176
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/DefaultSqlTypeMapping.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
* instance of a class implementing {@link SqlTypeMapping} interface can be set on the {@link Tables} class
3333
*
3434
* @author Kurt Niemi
35+
* @author Evgenii Koba
36+
* @author Jens Schauder
3537
* @since 3.2
3638
*/
3739
public class DefaultSqlTypeMapping implements SqlTypeMapping {
@@ -61,11 +63,11 @@ public DefaultSqlTypeMapping() {
6163

6264
@Override
6365
public String getColumnType(RelationalPersistentProperty property) {
64-
return typeMap.get(ClassUtils.resolvePrimitiveIfNecessary(property.getActualType()));
66+
return getColumnType(property.getActualType());
6567
}
6668

6769
@Override
68-
public String getColumnTypeByClass(Class clazz) {
69-
return typeMap.get(clazz);
70+
public String getColumnType(Class<?> type) {
71+
return typeMap.get(ClassUtils.resolvePrimitiveIfNecessary(type));
7072
}
7173
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/ForeignKey.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@
77
* Models a Foreign Key for generating SQL for Schema generation.
88
*
99
* @author Evgenii Koba
10-
* @since 3.2
10+
* @since 3.3
1111
*/
1212
record ForeignKey(String name, String tableName, List<String> columnNames, String referencedTableName,
13-
List<String> referencedColumnNames) {
13+
List<String> referencedColumnNames) {
1414
@Override
1515
public boolean equals(Object o) {
1616
if (this == o)
1717
return true;
1818
if (o == null || getClass() != o.getClass())
1919
return false;
2020
ForeignKey that = (ForeignKey) o;
21-
return Objects.equals(tableName, that.tableName) && Objects.equals(columnNames, that.columnNames) && Objects.equals(
22-
referencedTableName, that.referencedTableName) && Objects.equals(referencedColumnNames,
23-
that.referencedColumnNames);
21+
return Objects.equals(tableName, that.tableName) && Objects.equals(columnNames, that.columnNames)
22+
&& Objects.equals(referencedTableName, that.referencedTableName)
23+
&& Objects.equals(referencedColumnNames, that.referencedColumnNames);
2424
}
2525

2626
@Override

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/LiquibaseChangeSetWriter.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import java.util.function.BiPredicate;
5656
import java.util.function.Predicate;
5757
import java.util.stream.Collectors;
58+
import java.util.stream.Stream;
5859

5960
import org.springframework.core.io.Resource;
6061
import org.springframework.data.mapping.context.MappingContext;
@@ -90,6 +91,7 @@
9091
*
9192
* @author Kurt Niemi
9293
* @author Mark Paluch
94+
* @author Evgenii Koba
9395
* @since 3.2
9496
*/
9597
public class LiquibaseChangeSetWriter {
@@ -323,16 +325,18 @@ private ChangeSet createChangeSet(ChangeSetMetadata metadata, SchemaDiff differe
323325

324326
private SchemaDiff initial() {
325327

326-
Tables mappedEntities = Tables.from(mappingContext.getPersistentEntities().stream().filter(schemaFilter),
327-
sqlTypeMapping, null, mappingContext);
328+
Stream<? extends RelationalPersistentEntity<?>> entities = mappingContext.getPersistentEntities().stream()
329+
.filter(schemaFilter);
330+
Tables mappedEntities = Tables.from(entities, sqlTypeMapping, null, mappingContext);
328331
return SchemaDiff.diff(mappedEntities, Tables.empty(), nameComparator);
329332
}
330333

331334
private SchemaDiff differenceOf(Database database) throws LiquibaseException {
332335

333336
Tables existingTables = getLiquibaseModel(database);
334-
Tables mappedEntities = Tables.from(mappingContext.getPersistentEntities().stream().filter(schemaFilter),
335-
sqlTypeMapping, database.getDefaultCatalogName(), mappingContext);
337+
Stream<? extends RelationalPersistentEntity<?>> entities = mappingContext.getPersistentEntities().stream()
338+
.filter(schemaFilter);
339+
Tables mappedEntities = Tables.from(entities, sqlTypeMapping, database.getDefaultCatalogName(), mappingContext);
336340

337341
return SchemaDiff.diff(mappedEntities, existingTables, nameComparator);
338342
}
@@ -482,12 +486,15 @@ private Tables getLiquibaseModel(Database targetDatabase) throws LiquibaseExcept
482486
private static List<ForeignKey> extractForeignKeys(liquibase.structure.core.Table table) {
483487

484488
return table.getOutgoingForeignKeys().stream().map(foreignKey -> {
489+
485490
String tableName = foreignKey.getForeignKeyTable().getName();
486491
List<String> columnNames = foreignKey.getForeignKeyColumns().stream()
487492
.map(liquibase.structure.core.Column::getName).toList();
493+
488494
String referencedTableName = foreignKey.getPrimaryKeyTable().getName();
489495
List<String> referencedColumnNames = foreignKey.getPrimaryKeyColumns().stream()
490496
.map(liquibase.structure.core.Column::getName).toList();
497+
491498
return new ForeignKey(foreignKey.getName(), tableName, columnNames, referencedTableName, referencedColumnNames);
492499
}).collect(Collectors.toList());
493500
}
@@ -582,6 +589,7 @@ private static AddForeignKeyConstraintChange addForeignKey(ForeignKey foreignKey
582589
change.setBaseColumnNames(String.join(",", foreignKey.columnNames()));
583590
change.setReferencedTableName(foreignKey.referencedTableName());
584591
change.setReferencedColumnNames(String.join(",", foreignKey.referencedColumnNames()));
592+
585593
return change;
586594
}
587595

@@ -590,6 +598,7 @@ private static DropForeignKeyConstraintChange dropForeignKey(ForeignKey foreignK
590598
DropForeignKeyConstraintChange change = new DropForeignKeyConstraintChange();
591599
change.setConstraintName(foreignKey.name());
592600
change.setBaseTableName(foreignKey.tableName());
601+
593602
return change;
594603
}
595604

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SchemaDiff.java

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* or delete)
3232
*
3333
* @author Kurt Niemi
34+
* @author Evgenii Koba
3435
* @since 3.2
3536
*/
3637
record SchemaDiff(List<Table> tableAdditions, List<Table> tableDeletions, List<TableDiff> tableDiffs) {
@@ -120,6 +121,7 @@ private static List<TableDiff> diffTable(Tables mappedEntities, Map<String, Tabl
120121

121122
private static <T> Collection<T> findDiffs(Map<String, T> baseMapping, Map<String, T> toCompareMapping,
122123
Comparator<String> nameComparator) {
124+
123125
Map<String, T> diff = new TreeMap<>(nameComparator);
124126
diff.putAll(toCompareMapping);
125127
baseMapping.keySet().forEach(diff::remove);

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SqlTypeMapping.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
*
2626
* @author Kurt Niemi
2727
* @author Mark Paluch
28+
* @author Evgenii Koba
29+
* @author Jens Schauder
2830
* @since 3.2
2931
*/
3032
@FunctionalInterface
@@ -43,12 +45,14 @@ public interface SqlTypeMapping {
4345
/**
4446
* Determines a column type for Class.
4547
*
46-
* @param clazz class for which the type should be determined.
48+
* @param type class for which the type should be determined.
4749
* @return the SQL type to use, such as {@code VARCHAR} or {@code NUMERIC}. Can be {@literal null} if the strategy
4850
* cannot provide a column type.
51+
*
52+
* @since 3.3
4953
*/
5054
@Nullable
51-
default String getColumnTypeByClass(Class clazz) {
55+
default String getColumnType(Class<?> type) {
5256
return null;
5357
}
5458

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Table.java

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* Models a Table for generating SQL for Schema generation.
2727
*
2828
* @author Kurt Niemi
29+
* @author Evgenii Koba
2930
* @since 3.2
3031
*/
3132
record Table(@Nullable String schema, String name, List<Column> columns, List<ForeignKey> foreignKeys) {

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/TableDiff.java

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* target {@link Tables}.
2424
*
2525
* @author Kurt Niemi
26+
* @author Evgenii Koba
2627
* @since 3.2
2728
*/
2829
record TableDiff(Table table, List<Column> columnsToAdd, List<Column> columnsToDrop, List<ForeignKey> fkToAdd,

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Tables.java

+51-45
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Set;
2727
import java.util.stream.Collectors;
2828
import java.util.stream.Stream;
29+
2930
import org.springframework.data.annotation.Id;
3031
import org.springframework.data.mapping.context.MappingContext;
3132
import org.springframework.data.relational.core.mapping.MappedCollection;
@@ -34,11 +35,13 @@
3435
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
3536
import org.springframework.data.relational.core.sql.SqlIdentifier;
3637
import org.springframework.lang.Nullable;
38+
import org.springframework.util.Assert;
3739

3840
/**
3941
* Model class that contains Table/Column information that can be used to generate SQL for Schema generation.
4042
*
4143
* @author Kurt Niemi
44+
* @author Evgenii Koba
4245
* @since 3.2
4346
*/
4447
record Tables(List<Table> tables) {
@@ -71,7 +74,7 @@ public static Tables from(Stream<? extends RelationalPersistentEntity<?>> persis
7174
}
7275

7376
Column column = new Column(property.getColumnName().getReference(), sqlTypeMapping.getColumnType(property),
74-
sqlTypeMapping.isNullable(property), identifierColumns.contains(property));
77+
sqlTypeMapping.isNullable(property), identifierColumns.contains(property));
7578
table.columns().add(column);
7679
}
7780
return table;
@@ -92,34 +95,40 @@ public static Tables empty() {
9295
private static void applyForeignKeyMetadata(List<Table> tables, List<ForeignKeyMetadata> foreignKeyMetadataList) {
9396

9497
foreignKeyMetadataList.forEach(foreignKeyMetadata -> {
95-
Table table = tables.stream().filter(t -> t.name().equals(foreignKeyMetadata.tableName)).findAny().get();
98+
99+
Table table = tables.stream().filter(t -> t.name().equals(foreignKeyMetadata.tableName)).findAny().orElseThrow();
96100

97101
List<Column> parentIdColumns = collectParentIdentityColumns(foreignKeyMetadata, foreignKeyMetadataList, tables);
98102
List<String> parentIdColumnNames = parentIdColumns.stream().map(Column::name).toList();
99103

100104
String foreignKeyName = getForeignKeyName(foreignKeyMetadata.parentTableName, parentIdColumnNames);
101-
if(parentIdColumnNames.size() == 1) {
102-
addIfAbsent(table.columns(), new Column(foreignKeyMetadata.referencingColumnName(), parentIdColumns.get(0).type(),
103-
false, table.getIdColumns().isEmpty()));
104-
if(foreignKeyMetadata.keyColumnName() != null) {
105-
addIfAbsent(table.columns(), new Column(foreignKeyMetadata.keyColumnName(), foreignKeyMetadata.keyColumnType(),
106-
false, true));
105+
if (parentIdColumnNames.size() == 1) {
106+
107+
addIfAbsent(table.columns(), new Column(foreignKeyMetadata.referencingColumnName(),
108+
parentIdColumns.get(0).type(), false, table.getIdColumns().isEmpty()));
109+
if (foreignKeyMetadata.keyColumnName() != null) {
110+
addIfAbsent(table.columns(),
111+
new Column(foreignKeyMetadata.keyColumnName(), foreignKeyMetadata.keyColumnType(), false, true));
107112
}
108-
addIfAbsent(table.foreignKeys(), new ForeignKey(foreignKeyName, foreignKeyMetadata.tableName(),
109-
List.of(foreignKeyMetadata.referencingColumnName()), foreignKeyMetadata.parentTableName(), parentIdColumnNames));
113+
addIfAbsent(table.foreignKeys(),
114+
new ForeignKey(foreignKeyName, foreignKeyMetadata.tableName(),
115+
List.of(foreignKeyMetadata.referencingColumnName()), foreignKeyMetadata.parentTableName(),
116+
parentIdColumnNames));
110117
} else {
118+
111119
addIfAbsent(table.columns(), parentIdColumns.toArray(new Column[0]));
112-
addIfAbsent(table.columns(), new Column(foreignKeyMetadata.keyColumnName(), foreignKeyMetadata.keyColumnType(),
113-
false, true));
114-
addIfAbsent(table.foreignKeys(), new ForeignKey(foreignKeyName, foreignKeyMetadata.tableName(), parentIdColumnNames,
115-
foreignKeyMetadata.parentTableName(), parentIdColumnNames));
120+
addIfAbsent(table.columns(),
121+
new Column(foreignKeyMetadata.keyColumnName(), foreignKeyMetadata.keyColumnType(), false, true));
122+
addIfAbsent(table.foreignKeys(), new ForeignKey(foreignKeyName, foreignKeyMetadata.tableName(),
123+
parentIdColumnNames, foreignKeyMetadata.parentTableName(), parentIdColumnNames));
116124
}
117125

118126
});
119127
}
120128

121-
private static <E> void addIfAbsent(List<E> list, E... elements) {
122-
for(E element : elements) {
129+
private static <E> void addIfAbsent(List<E> list, E... elements) {
130+
131+
for (E element : elements) {
123132
if (!list.contains(element)) {
124133
list.add(element);
125134
}
@@ -137,26 +146,28 @@ private static List<Column> collectParentIdentityColumns(ForeignKeyMetadata chil
137146
excludeTables.add(child.tableName());
138147

139148
Table parentTable = findTableByName(tables, child.parentTableName());
140-
ForeignKeyMetadata parentMetadata = findMetadataByTableName(foreignKeyMetadataList, child.parentTableName(), excludeTables);
149+
ForeignKeyMetadata parentMetadata = findMetadataByTableName(foreignKeyMetadataList, child.parentTableName(),
150+
excludeTables);
141151
List<Column> parentIdColumns = parentTable.getIdColumns();
142152

143153
if (!parentIdColumns.isEmpty()) {
144154
return new ArrayList<>(parentIdColumns);
145-
} else if(parentMetadata == null) {
146-
//mustn't happen, probably wrong entity declaration
147-
return new ArrayList<>();
148-
} else {
149-
List<Column> parentParentIdColumns = collectParentIdentityColumns(parentMetadata, foreignKeyMetadataList, tables);
150-
if (parentParentIdColumns.size() == 1) {
151-
Column parentParentIdColumn = parentParentIdColumns.get(0);
152-
Column withChangedName = new Column(parentMetadata.referencingColumnName, parentParentIdColumn.type(), false, true);
153-
parentParentIdColumns = new LinkedList<>(List.of(withChangedName));
154-
}
155-
if (parentMetadata.keyColumnName() != null) {
156-
parentParentIdColumns.add(new Column(parentMetadata.keyColumnName(), parentMetadata.keyColumnType(), false, true));
157-
}
158-
return parentParentIdColumns;
159155
}
156+
157+
Assert.state(parentMetadata != null, "parentMetadata must not be null at this stage");
158+
159+
List<Column> parentParentIdColumns = collectParentIdentityColumns(parentMetadata, foreignKeyMetadataList, tables);
160+
if (parentParentIdColumns.size() == 1) {
161+
Column parentParentIdColumn = parentParentIdColumns.get(0);
162+
Column withChangedName = new Column(parentMetadata.referencingColumnName, parentParentIdColumn.type(), false,
163+
true);
164+
parentParentIdColumns = new LinkedList<>(List.of(withChangedName));
165+
}
166+
if (parentMetadata.keyColumnName() != null) {
167+
parentParentIdColumns
168+
.add(new Column(parentMetadata.keyColumnName(), parentMetadata.keyColumnType(), false, true));
169+
}
170+
return parentParentIdColumns;
160171
}
161172

162173
@Nullable
@@ -167,45 +178,40 @@ private static Table findTableByName(List<Table> tables, String tableName) {
167178
@Nullable
168179
private static ForeignKeyMetadata findMetadataByTableName(List<ForeignKeyMetadata> metadata, String tableName,
169180
Set<String> excludeTables) {
181+
170182
return metadata.stream()
171-
.filter(m -> m.tableName().equals(tableName) && !excludeTables.contains(m.parentTableName()))
172-
.findAny()
183+
.filter(m -> m.tableName().equals(tableName) && !excludeTables.contains(m.parentTableName())).findAny()
173184
.orElse(null);
174185
}
175186

176-
private static ForeignKeyMetadata createForeignKeyMetadata(
177-
RelationalPersistentEntity<?> entity,
187+
private static ForeignKeyMetadata createForeignKeyMetadata(RelationalPersistentEntity<?> entity,
178188
RelationalPersistentProperty property,
179189
MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> context,
180190
SqlTypeMapping sqlTypeMapping) {
181191

182192
RelationalPersistentEntity childEntity = context.getRequiredPersistentEntity(property.getActualType());
183193

184194
String referencedKeyColumnType = null;
185-
if(property.isAnnotationPresent(MappedCollection.class)) {
195+
if (property.isAnnotationPresent(MappedCollection.class)) {
186196
if (property.getType() == List.class) {
187-
referencedKeyColumnType = sqlTypeMapping.getColumnTypeByClass(Integer.class);
197+
referencedKeyColumnType = sqlTypeMapping.getColumnType(Integer.class);
188198
} else if (property.getType() == Map.class) {
189-
referencedKeyColumnType = sqlTypeMapping.getColumnTypeByClass(property.getComponentType());
199+
referencedKeyColumnType = sqlTypeMapping.getColumnType(property.getComponentType());
190200
}
191201
}
192202

193-
return new ForeignKeyMetadata(
194-
childEntity.getTableName().getReference(),
203+
return new ForeignKeyMetadata(childEntity.getTableName().getReference(),
195204
property.getReverseColumnName(entity).getReference(),
196205
Optional.ofNullable(property.getKeyColumn()).map(SqlIdentifier::getReference).orElse(null),
197-
referencedKeyColumnType,
198-
entity.getTableName().getReference()
199-
);
206+
referencedKeyColumnType, entity.getTableName().getReference());
200207
}
201208

202-
//TODO should we place it in BasicRelationalPersistentProperty/BasicRelationalPersistentEntity and generate using NamingStrategy?
203209
private static String getForeignKeyName(String referencedTableName, List<String> referencedColumnNames) {
204210
return String.format("%s_%s_fk", referencedTableName, String.join("_", referencedColumnNames));
205211
}
206212

207213
private record ForeignKeyMetadata(String tableName, String referencingColumnName, @Nullable String keyColumnName,
208-
@Nullable String keyColumnType, String parentTableName) {
214+
@Nullable String keyColumnType, String parentTableName) {
209215

210216
}
211217
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/schema/LiquibaseChangeSetWriterIntegrationTests.java

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
* Integration tests for {@link LiquibaseChangeSetWriter}.
5656
*
5757
* @author Mark Paluch
58+
* @author Evgenii Koba
5859
*/
5960
class LiquibaseChangeSetWriterIntegrationTests {
6061

0 commit comments

Comments
 (0)