From 048ba4fe25db5636f16a2fd834dd6b8f532a1945 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 21 Jul 2022 15:44:17 +0200 Subject: [PATCH 1/4] Prepare issue branch. --- pom.xml | 2 +- spring-data-jdbc-distribution/pom.xml | 2 +- spring-data-jdbc/pom.xml | 4 ++-- spring-data-r2dbc/pom.xml | 4 ++-- spring-data-relational/pom.xml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 8859e9d37e..1753af237c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index db3b7ddd1a..97f9e0a142 100644 --- a/spring-data-jdbc-distribution/pom.xml +++ b/spring-data-jdbc-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index 547ff62b8b..cab3dc0afe 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-jdbc - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT diff --git a/spring-data-r2dbc/pom.xml b/spring-data-r2dbc/pom.xml index 48d57abbde..b6afafe8a0 100644 --- a/spring-data-r2dbc/pom.xml +++ b/spring-data-r2dbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-r2dbc - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT Spring Data R2DBC Spring Data module for R2DBC @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 717ac86edf..918b40ef4b 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-relational - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 3.0.0-SNAPSHOT + 3.0.0-GH-1287-SNAPSHOT From f643ced55b9ce6db2fcad22d9c91052709232b53 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 21 Jul 2022 15:45:08 +0200 Subject: [PATCH 2/4] Use OFFSET/FETCH syntax in H2 dialect. As per suggestion of the H2 maintainer. --- .../springframework/data/relational/core/dialect/H2Dialect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/H2Dialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/H2Dialect.java index 879aa8ecd3..f7bec039f3 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/H2Dialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/H2Dialect.java @@ -56,7 +56,7 @@ public String getOffset(long offset) { @Override public String getLimitOffset(long limit, long offset) { - return String.format("LIMIT %d OFFSET %d", limit, offset); + return String.format("OFFSET %d ROWS FETCH FIRST %d ROWS ONLY", offset, limit); } @Override From b110d7b7bd1e56e4478c300ed7806e2acf410e95 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 21 Jul 2022 15:45:30 +0200 Subject: [PATCH 3/4] Polishing. Reuse H2 dialect settings in R2DBC-specific H2 dialect. --- .../springframework/data/r2dbc/dialect/H2Dialect.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java index 1409d4720e..4a6e3c3241 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java @@ -1,6 +1,6 @@ package org.springframework.data.r2dbc.dialect; -import org.springframework.data.relational.core.dialect.AnsiDialect; +import org.springframework.data.relational.core.dialect.LimitClause; import org.springframework.data.relational.core.dialect.LockClause; import org.springframework.data.relational.core.sql.SqlIdentifier; @@ -25,10 +25,11 @@ public String renderForGeneratedValues(SqlIdentifier identifier) { @Override public LockClause lock() { - // H2 Dialect does not support the same lock keywords as PostgreSQL, but it supports the ANSI SQL standard. - // see https://www.h2database.com/html/commands.html - // and https://www.h2database.com/html/features.html#compatibility - return AnsiDialect.INSTANCE.lock(); + return org.springframework.data.relational.core.dialect.H2Dialect.INSTANCE.lock(); } + @Override + public LimitClause limit() { + return org.springframework.data.relational.core.dialect.H2Dialect.INSTANCE.limit(); + } } From 7384392b81763adfcf9d2ad625b7c275c8f4acde Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 25 Jul 2022 14:45:26 +0200 Subject: [PATCH 4/4] Polishing. Refactor ArrayColumns support classes, into toplevel-types. Let R2DBC H2 sublass the relational H2Dialect to decouple from Postgres. --- .../jdbc/core/convert/JdbcArrayColumns.java | 9 +--- .../core/dialect/JdbcPostgresDialect.java | 6 +-- .../data/r2dbc/dialect/H2Dialect.java | 42 +++++++++++++---- .../data/r2dbc/dialect/PostgresDialect.java | 38 ++------------- .../r2dbc/dialect/SimpleTypeArrayColumns.java | 47 +++++++++++++++++++ .../relational/core/dialect/AnsiDialect.java | 20 +------- .../relational/core/dialect/ArrayColumns.java | 17 +++++++ .../core/dialect/ObjectArrayColumns.java | 44 +++++++++++++++++ .../core/dialect/PostgresDialect.java | 21 +-------- 9 files changed, 151 insertions(+), 93 deletions(-) create mode 100644 spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/SimpleTypeArrayColumns.java create mode 100644 spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ObjectArrayColumns.java diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcArrayColumns.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcArrayColumns.java index ece2384fae..a9b3ea19b0 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcArrayColumns.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcArrayColumns.java @@ -23,19 +23,14 @@ * {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC-specific functionality. * * @author Jens Schauder + * @author Mark Paluch * @since 2.3 */ public interface JdbcArrayColumns extends ArrayColumns { @Override default Class getArrayType(Class userType) { - - Class componentType = userType; - while (componentType.isArray()) { - componentType = componentType.getComponentType(); - } - - return componentType; + return ArrayColumns.unwrapComponentType(userType); } /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java index c5e97cf9ef..43b4c72f9c 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java @@ -38,11 +38,11 @@ public JdbcArrayColumns getArraySupport() { return ARRAY_COLUMNS; } - static class JdbcPostgresArrayColumns extends PostgresArrayColumns implements JdbcArrayColumns { + static class JdbcPostgresArrayColumns implements JdbcArrayColumns { @Override - public Class getArrayType(Class userType) { - return JdbcArrayColumns.super.getArrayType(userType); + public boolean isSupported() { + return true; } @Override diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java index 4a6e3c3241..49c2adea45 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/H2Dialect.java @@ -1,35 +1,57 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed 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 + * + * https://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. + */ package org.springframework.data.r2dbc.dialect; -import org.springframework.data.relational.core.dialect.LimitClause; -import org.springframework.data.relational.core.dialect.LockClause; +import org.springframework.data.relational.core.dialect.ArrayColumns; +import org.springframework.data.relational.core.dialect.ObjectArrayColumns; import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.data.util.Lazy; +import org.springframework.r2dbc.core.binding.BindMarkersFactory; /** - * An SQL dialect for H2 in Postgres Compatibility mode. + * R2DBC dialect for H2. * * @author Mark Paluch * @author Jens Schauder * @author Diego Krupitza */ -public class H2Dialect extends PostgresDialect { +public class H2Dialect extends org.springframework.data.relational.core.dialect.H2Dialect implements R2dbcDialect { /** * Singleton instance. */ public static final H2Dialect INSTANCE = new H2Dialect(); + private static final BindMarkersFactory INDEXED = BindMarkersFactory.indexed("$", 1); + + private final Lazy arrayColumns = Lazy + .of(() -> new SimpleTypeArrayColumns(ObjectArrayColumns.INSTANCE, getSimpleTypeHolder())); + @Override - public String renderForGeneratedValues(SqlIdentifier identifier) { - return identifier.getReference(getIdentifierProcessing()); + public BindMarkersFactory getBindMarkersFactory() { + return INDEXED; } @Override - public LockClause lock() { - return org.springframework.data.relational.core.dialect.H2Dialect.INSTANCE.lock(); + public String renderForGeneratedValues(SqlIdentifier identifier) { + return identifier.getReference(getIdentifierProcessing()); } @Override - public LimitClause limit() { - return org.springframework.data.relational.core.dialect.H2Dialect.INSTANCE.limit(); + public ArrayColumns getArraySupport() { + return this.arrayColumns.get(); } } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/PostgresDialect.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/PostgresDialect.java index 9bb861585e..8cc1022934 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/PostgresDialect.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/PostgresDialect.java @@ -24,8 +24,8 @@ import org.springframework.data.geo.Circle; import org.springframework.data.geo.Point; import org.springframework.data.geo.Polygon; -import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.relational.core.dialect.ArrayColumns; +import org.springframework.data.relational.core.dialect.ObjectArrayColumns; import org.springframework.data.util.Lazy; import org.springframework.lang.NonNull; import org.springframework.r2dbc.core.binding.BindMarkersFactory; @@ -79,9 +79,8 @@ public class PostgresDialect extends org.springframework.data.relational.core.di private static final BindMarkersFactory INDEXED = BindMarkersFactory.indexed("$", 1); - private final Lazy arrayColumns = Lazy.of(() -> new R2dbcArrayColumns( - org.springframework.data.relational.core.dialect.PostgresDialect.INSTANCE.getArraySupport(), - getSimpleTypeHolder())); + private final Lazy arrayColumns = Lazy + .of(() -> new SimpleTypeArrayColumns(ObjectArrayColumns.INSTANCE, getSimpleTypeHolder())); /* * (non-Javadoc) @@ -137,37 +136,6 @@ public Collection getConverters() { return converters; } - private static class R2dbcArrayColumns implements ArrayColumns { - - private final ArrayColumns delegate; - private final SimpleTypeHolder simpleTypeHolder; - - R2dbcArrayColumns(ArrayColumns delegate, SimpleTypeHolder simpleTypeHolder) { - this.delegate = delegate; - this.simpleTypeHolder = simpleTypeHolder; - } - - @Override - public boolean isSupported() { - return this.delegate.isSupported(); - } - - @Override - public Class getArrayType(Class userType) { - - Class typeToUse = userType; - while (typeToUse.getComponentType() != null) { - typeToUse = typeToUse.getComponentType(); - } - - if (!this.simpleTypeHolder.isSimpleType(typeToUse)) { - throw new IllegalArgumentException("Unsupported array type: " + ClassUtils.getQualifiedName(typeToUse)); - } - - return this.delegate.getArrayType(typeToUse); - } - } - /** * If the class is present on the class path, invoke the specified consumer {@code action} with the class object, * otherwise do nothing. diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/SimpleTypeArrayColumns.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/SimpleTypeArrayColumns.java new file mode 100644 index 0000000000..5918487571 --- /dev/null +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/SimpleTypeArrayColumns.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed 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 + * + * https://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. + */ +package org.springframework.data.r2dbc.dialect; + +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.relational.core.dialect.ArrayColumns; +import org.springframework.util.ClassUtils; + +/** + * {@link ArrayColumns} support based on {@link SimpleTypeHolder store-native simple types}. + * + * @author Mark Paluch + * @since 3.0 + */ +record SimpleTypeArrayColumns(ArrayColumns delegate, SimpleTypeHolder simpleTypeHolder) implements ArrayColumns { + + @Override + public boolean isSupported() { + return this.delegate.isSupported(); + } + + @Override + public Class getArrayType(Class userType) { + + Class typeToUse = ArrayColumns.unwrapComponentType(userType); + + if (!this.simpleTypeHolder.isSimpleType(typeToUse)) { + throw new IllegalArgumentException( + "Unsupported array type: %s".formatted(ClassUtils.getQualifiedName(typeToUse))); + } + + return this.delegate.getArrayType(typeToUse); + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/AnsiDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/AnsiDialect.java index 90e5e0c6ca..cb72d24969 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/AnsiDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/AnsiDialect.java @@ -17,8 +17,6 @@ import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.LockOptions; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; /** * An SQL dialect for the ANSI SQL standard. @@ -72,7 +70,7 @@ public Position getClausePosition() { } }; - private final AnsiArrayColumns ARRAY_COLUMNS = new AnsiArrayColumns(); + private final ArrayColumns ARRAY_COLUMNS = ObjectArrayColumns.INSTANCE; @Override public LimitClause limit() { @@ -89,22 +87,6 @@ public ArrayColumns getArraySupport() { return ARRAY_COLUMNS; } - static class AnsiArrayColumns implements ArrayColumns { - - @Override - public boolean isSupported() { - return true; - } - - @Override - public Class getArrayType(Class userType) { - - Assert.notNull(userType, "Array component type must not be null"); - - return ClassUtils.resolvePrimitiveIfNecessary(userType); - } - } - @Override public IdentifierProcessing getIdentifierProcessing() { return IdentifierProcessing.ANSI; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ArrayColumns.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ArrayColumns.java index 897cad8a97..1cc7967e7c 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ArrayColumns.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ArrayColumns.java @@ -58,4 +58,21 @@ public Class getArrayType(Class userType) { throw new UnsupportedOperationException("Array types not supported"); } } + + /** + * Unwrap the nested {@link Class#getComponentType()} from a given {@link Class}. + * + * @param clazz the type to inspect. + * @return the unwrapped component type. + * @since 3.0 + */ + static Class unwrapComponentType(Class clazz) { + + Class componentType = clazz; + while (componentType.isArray()) { + componentType = componentType.getComponentType(); + } + + return componentType; + } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ObjectArrayColumns.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ObjectArrayColumns.java new file mode 100644 index 0000000000..414214ccdc --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/ObjectArrayColumns.java @@ -0,0 +1,44 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed 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 + * + * https://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. + */ +package org.springframework.data.relational.core.dialect; + +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * {@link ArrayColumns} support using the actual object type or {@link Class#isPrimitive() boxed primitives} Java types. + * + * @author Mark Paluch + * @since 3.0 + * @see ClassUtils#resolvePrimitiveIfNecessary + */ +public class ObjectArrayColumns implements ArrayColumns { + + public static final ObjectArrayColumns INSTANCE = new ObjectArrayColumns(); + + @Override + public boolean isSupported() { + return true; + } + + @Override + public Class getArrayType(Class userType) { + + Assert.notNull(userType, "Array component type must not be null"); + + return ClassUtils.resolvePrimitiveIfNecessary(ArrayColumns.unwrapComponentType(userType)); + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java index 33a73e2639..bcc987f681 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java @@ -32,7 +32,6 @@ import org.springframework.data.relational.core.sql.SimpleFunction; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.TableLike; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** @@ -76,7 +75,7 @@ public Position getClausePosition() { } }; - private final PostgresArrayColumns ARRAY_COLUMNS = new PostgresArrayColumns(); + private static final ObjectArrayColumns ARRAY_COLUMNS = ObjectArrayColumns.INSTANCE; @Override public LimitClause limit() { @@ -146,22 +145,6 @@ public Position getClausePosition() { } } - protected static class PostgresArrayColumns implements ArrayColumns { - - @Override - public boolean isSupported() { - return true; - } - - @Override - public Class getArrayType(Class userType) { - - Assert.notNull(userType, "Array component type must not be null"); - - return ClassUtils.resolvePrimitiveIfNecessary(userType); - } - } - @Override public IdentifierProcessing getIdentifierProcessing() { return IdentifierProcessing.create(Quoting.ANSI, LetterCasing.LOWER_CASE); @@ -179,7 +162,7 @@ public Set> simpleTypes() { "org.postgresql.geometric.PGline", // "org.postgresql.geometric.PGpath", // "org.postgresql.geometric.PGpolygon", // - "org.postgresql.geometric.PGlseg" // + "org.postgresql.geometric.PGlseg" // ); simpleTypeNames.forEach(name -> ifClassPresent(name, simpleTypes::add)); return Collections.unmodifiableSet(simpleTypes);