Polishing.

Reuse H2 dialect settings in R2DBC-specific H2 dialect.
Refactor ArrayColumns support classes, into toplevel-types. Let R2DBC H2 sublass the relational H2Dialect to decouple from Postgres.

See #1287
Original pull request #1297
This commit is contained in:
Mark Paluch
2022-07-21 15:45:30 +02:00
committed by Jens Schauder
parent c2e163b5c2
commit 0d1ba211c5
9 changed files with 152 additions and 93 deletions

View File

@@ -23,19 +23,14 @@ import org.springframework.data.relational.core.dialect.ArrayColumns;
* {@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);
}
/**

View File

@@ -38,11 +38,11 @@ public class JdbcPostgresDialect extends PostgresDialect implements JdbcDialect
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

View File

@@ -1,34 +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.AnsiDialect;
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> arrayColumns = Lazy
.of(() -> new SimpleTypeArrayColumns(ObjectArrayColumns.INSTANCE, getSimpleTypeHolder()));
@Override
public BindMarkersFactory getBindMarkersFactory() {
return INDEXED;
}
@Override
public String renderForGeneratedValues(SqlIdentifier identifier) {
return identifier.getReference(getIdentifierProcessing());
}
@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();
public ArrayColumns getArraySupport() {
return this.arrayColumns.get();
}
}

View File

@@ -24,8 +24,8 @@ import org.springframework.data.geo.Box;
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> arrayColumns = Lazy.of(() -> new R2dbcArrayColumns(
org.springframework.data.relational.core.dialect.PostgresDialect.INSTANCE.getArraySupport(),
getSimpleTypeHolder()));
private final Lazy<ArrayColumns> arrayColumns = Lazy
.of(() -> new SimpleTypeArrayColumns(ObjectArrayColumns.INSTANCE, getSimpleTypeHolder()));
/*
* (non-Javadoc)
@@ -137,37 +136,6 @@ public class PostgresDialect extends org.springframework.data.relational.core.di
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.

View File

@@ -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);
}
}

View File

@@ -17,8 +17,6 @@ package org.springframework.data.relational.core.dialect;
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 class AnsiDialect extends AbstractDialect {
}
};
private final AnsiArrayColumns ARRAY_COLUMNS = new AnsiArrayColumns();
private final ArrayColumns ARRAY_COLUMNS = ObjectArrayColumns.INSTANCE;
@Override
public LimitClause limit() {
@@ -89,22 +87,6 @@ public class AnsiDialect extends AbstractDialect {
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;

View File

@@ -58,4 +58,21 @@ public interface ArrayColumns {
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;
}
}

View File

@@ -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));
}
}

View File

@@ -32,7 +32,6 @@ import org.springframework.data.relational.core.sql.SQL;
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 class PostgresDialect extends AbstractDialect {
}
};
private final PostgresArrayColumns ARRAY_COLUMNS = new PostgresArrayColumns();
private static final ObjectArrayColumns ARRAY_COLUMNS = ObjectArrayColumns.INSTANCE;
@Override
public LimitClause limit() {
@@ -146,22 +145,6 @@ public class PostgresDialect extends AbstractDialect {
}
}
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 class PostgresDialect extends AbstractDialect {
"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);