Browse Source

Polishing.

Deprecate original DialectResolver and JdbcArrayColumns as they've been in the wrong package and introduce replacements in the dialect package. Let deprecated types extend from their replacements to retain compatibility.

Make instance holders final, fix Javadoc typos, update reference docs.

Original pull request #2036
See #2031
pull/2065/head
Mark Paluch 8 months ago committed by Jens Schauder
parent
commit
becc753e2f
No known key found for this signature in database
GPG Key ID: 2BE5D185CD2A1CE6
  1. 24
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
  2. 4
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcArrayColumns.java
  3. 272
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java
  4. 92
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumns.java
  5. 38
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumnsAdapter.java
  6. 2
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java
  7. 2
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java
  8. 12
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java
  9. 12
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcHsqlDbDialect.java
  10. 5
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMariaDbDialect.java
  11. 5
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java
  12. 11
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcOracleDialect.java
  13. 1
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java
  14. 3
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java
  15. 4
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java
  16. 117
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java
  17. 2
      spring-data-jdbc/src/main/resources/META-INF/spring.factories
  18. 2
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java
  19. 4
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java
  20. 4
      spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/AnsiDialect.java
  21. 8
      spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/H2Dialect.java
  22. 6
      src/main/antora/modules/ROOT/pages/jdbc/getting-started.adoc

24
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java

@ -33,7 +33,7 @@ import org.springframework.util.Assert; @@ -33,7 +33,7 @@ import org.springframework.util.Assert;
public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
private final JdbcOperations operations;
private final JdbcArrayColumns arrayColumns;
private final org.springframework.data.jdbc.core.dialect.JdbcArrayColumns arrayColumns;
/**
* Creates a new {@link DefaultJdbcTypeFactory}.
@ -41,7 +41,7 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory { @@ -41,7 +41,7 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
* @param operations must not be {@literal null}.
*/
public DefaultJdbcTypeFactory(JdbcOperations operations) {
this(operations, JdbcArrayColumns.DefaultSupport.INSTANCE);
this(operations, org.springframework.data.jdbc.core.dialect.JdbcArrayColumns.DefaultSupport.INSTANCE);
}
/**
@ -49,7 +49,11 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory { @@ -49,7 +49,11 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
*
* @param operations must not be {@literal null}.
* @since 2.3
* @deprecated use
* {@link #DefaultJdbcTypeFactory(JdbcOperations, org.springframework.data.jdbc.core.dialect.JdbcArrayColumns)}
* instead.
*/
@Deprecated(forRemoval = true, since = "3.5")
public DefaultJdbcTypeFactory(JdbcOperations operations, JdbcArrayColumns arrayColumns) {
Assert.notNull(operations, "JdbcOperations must not be null");
@ -59,6 +63,22 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory { @@ -59,6 +63,22 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
this.arrayColumns = arrayColumns;
}
/**
* Creates a new {@link DefaultJdbcTypeFactory}.
*
* @param operations must not be {@literal null}.
* @since 3.5
*/
public DefaultJdbcTypeFactory(JdbcOperations operations,
org.springframework.data.jdbc.core.dialect.JdbcArrayColumns arrayColumns) {
Assert.notNull(operations, "JdbcOperations must not be null");
Assert.notNull(arrayColumns, "JdbcArrayColumns must not be null");
this.operations = operations;
this.arrayColumns = arrayColumns;
}
@Override
public Array createArray(Object[] value) {

4
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcArrayColumns.java

@ -26,8 +26,10 @@ import org.springframework.data.relational.core.dialect.ArrayColumns; @@ -26,8 +26,10 @@ import org.springframework.data.relational.core.dialect.ArrayColumns;
* @author Jens Schauder
* @author Mark Paluch
* @since 2.3
* @deprecated since 3.5, replacement moved to {@link org.springframework.data.jdbc.core.dialect.JdbcArrayColumns}.
*/
public interface JdbcArrayColumns extends ArrayColumns {
@Deprecated(forRemoval = true)
public interface JdbcArrayColumns extends org.springframework.data.jdbc.core.dialect.JdbcArrayColumns {
@Override
default Class<?> getArrayType(Class<?> userType) {

272
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java

@ -0,0 +1,272 @@ @@ -0,0 +1,272 @@
/*
* Copyright 2020-2025 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.jdbc.core.dialect;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.Escaper;
import org.springframework.data.relational.core.dialect.IdGeneration;
import org.springframework.data.relational.core.dialect.InsertRenderContext;
import org.springframework.data.relational.core.dialect.LimitClause;
import org.springframework.data.relational.core.dialect.LockClause;
import org.springframework.data.relational.core.dialect.OrderByNullPrecedence;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.SimpleFunction;
import org.springframework.data.relational.core.sql.render.SelectRenderContext;
import org.springframework.data.util.Optionals;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Resolves a {@link Dialect}. Resolution typically uses {@link JdbcOperations} to obtain and inspect a
* {@link Connection}. Dialect resolution uses Spring's {@link SpringFactoriesLoader spring.factories} to determine
* available {@link JdbcDialectProvider extensions}.
*
* @author Jens Schauder
* @author Mikhail Polivakha
* @since 3.5
* @see Dialect
* @see SpringFactoriesLoader
*/
public class DialectResolver {
private static final Log LOG = LogFactory.getLog(DialectResolver.class);
private static final List<JdbcDialectProvider> DETECTORS = SpringFactoriesLoader
.loadFactories(JdbcDialectProvider.class, DialectResolver.class.getClassLoader());
private static final List<org.springframework.data.jdbc.repository.config.DialectResolver.JdbcDialectProvider> LEGACY_DETECTORS = SpringFactoriesLoader
.loadFactories(org.springframework.data.jdbc.repository.config.DialectResolver.JdbcDialectProvider.class,
DialectResolver.class.getClassLoader());
// utility constructor.
private DialectResolver() {}
/**
* Retrieve a {@link Dialect} by inspecting a {@link Connection}.
*
* @param operations must not be {@literal null}.
* @return the resolved {@link Dialect} {@link NoDialectException} if the database type cannot be determined from
* {@link DataSource}.
* @throws NoDialectException if no {@link Dialect} can be found.
*/
public static JdbcDialect getDialect(JdbcOperations operations) {
return Stream.concat(LEGACY_DETECTORS.stream(), DETECTORS.stream()) //
.map(it -> it.getDialect(operations)) //
.flatMap(Optionals::toStream) //
.map(it -> it instanceof JdbcDialect ? (JdbcDialect) it : new JdbcDialectAdapter(it)).findFirst() //
.orElseThrow(() -> new NoDialectException(
String.format("Cannot determine a dialect for %s; Please provide a Dialect", operations)));
}
/**
* SPI to extend Spring's default JDBC Dialect discovery mechanism. Implementations of this interface are discovered
* through Spring's {@link SpringFactoriesLoader} mechanism.
*
* @author Jens Schauder
* @see SpringFactoriesLoader
*/
public interface JdbcDialectProvider {
/**
* Returns a {@link Dialect} for a {@link DataSource}.
*
* @param operations the {@link JdbcOperations} to be used with the {@link Dialect}.
* @return {@link Optional} containing the {@link Dialect} if the {@link JdbcDialectProvider} can provide a dialect
* object, otherwise {@link Optional#empty()}.
*/
Optional<Dialect> getDialect(JdbcOperations operations);
}
public static class DefaultDialectProvider implements JdbcDialectProvider {
@Override
public Optional<Dialect> getDialect(JdbcOperations operations) {
return Optional.ofNullable(operations.execute((ConnectionCallback<Dialect>) DefaultDialectProvider::getDialect));
}
@Nullable
private static JdbcDialect getDialect(Connection connection) throws SQLException {
DatabaseMetaData metaData = connection.getMetaData();
String name = metaData.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
if (name.contains("hsql")) {
return JdbcHsqlDbDialect.INSTANCE;
}
if (name.contains("h2")) {
return JdbcH2Dialect.INSTANCE;
}
if (name.contains("mysql")) {
return new JdbcMySqlDialect(getIdentifierProcessing(metaData));
}
if (name.contains("mariadb")) {
return new JdbcMariaDbDialect(getIdentifierProcessing(metaData));
}
if (name.contains("postgresql")) {
return JdbcPostgresDialect.INSTANCE;
}
if (name.contains("microsoft")) {
return JdbcSqlServerDialect.INSTANCE;
}
if (name.contains("db2")) {
return JdbcDb2Dialect.INSTANCE;
}
if (name.contains("oracle")) {
return JdbcOracleDialect.INSTANCE;
}
LOG.info(String.format("Couldn't determine Dialect for \"%s\"", name));
return null;
}
private static IdentifierProcessing getIdentifierProcessing(DatabaseMetaData metaData) throws SQLException {
// getIdentifierQuoteString() returns a space " " if identifier quoting is not
// supported.
String quoteString = metaData.getIdentifierQuoteString();
IdentifierProcessing.Quoting quoting = StringUtils.hasText(quoteString)
? new IdentifierProcessing.Quoting(quoteString)
: IdentifierProcessing.Quoting.NONE;
IdentifierProcessing.LetterCasing letterCasing;
// IdentifierProcessing tries to mimic the behavior of unquoted identifiers for their quoted variants.
if (metaData.supportsMixedCaseIdentifiers()) {
letterCasing = IdentifierProcessing.LetterCasing.AS_IS;
} else if (metaData.storesUpperCaseIdentifiers()) {
letterCasing = IdentifierProcessing.LetterCasing.UPPER_CASE;
} else if (metaData.storesLowerCaseIdentifiers()) {
letterCasing = IdentifierProcessing.LetterCasing.LOWER_CASE;
} else { // this shouldn't happen since one of the previous cases should be true.
// But if it does happen, we go with the ANSI default.
letterCasing = IdentifierProcessing.LetterCasing.UPPER_CASE;
}
return IdentifierProcessing.create(quoting, letterCasing);
}
}
/**
* Exception thrown when {@link DialectResolver} cannot resolve a {@link Dialect}.
*/
public static class NoDialectException extends NonTransientDataAccessException {
/**
* Constructor for NoDialectFoundException.
*
* @param msg the detail message
*/
protected NoDialectException(String msg) {
super(msg);
}
}
private static class JdbcDialectAdapter implements JdbcDialect {
private final Dialect delegate;
private final JdbcArrayColumnsAdapter arrayColumns;
public JdbcDialectAdapter(Dialect delegate) {
this.delegate = delegate;
this.arrayColumns = new JdbcArrayColumnsAdapter(delegate.getArraySupport());
}
@Override
public LimitClause limit() {
return delegate.limit();
}
@Override
public LockClause lock() {
return delegate.lock();
}
@Override
public JdbcArrayColumns getArraySupport() {
return arrayColumns;
}
@Override
public SelectRenderContext getSelectContext() {
return delegate.getSelectContext();
}
@Override
public IdentifierProcessing getIdentifierProcessing() {
return delegate.getIdentifierProcessing();
}
@Override
public Escaper getLikeEscaper() {
return delegate.getLikeEscaper();
}
@Override
public IdGeneration getIdGeneration() {
return delegate.getIdGeneration();
}
@Override
public Collection<Object> getConverters() {
return delegate.getConverters();
}
@Override
public Set<Class<?>> simpleTypes() {
return delegate.simpleTypes();
}
@Override
public InsertRenderContext getInsertRenderContext() {
return delegate.getInsertRenderContext();
}
@Override
public OrderByNullPrecedence orderByNullHandling() {
return delegate.orderByNullHandling();
}
@Override
public SimpleFunction getExistsFunction() {
return delegate.getExistsFunction();
}
@Override
public boolean supportsSingleQueryLoading() {
return delegate.supportsSingleQueryLoading();
}
}
}

92
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumns.java

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
/*
* Copyright 2021-2025 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.jdbc.core.dialect;
import java.sql.SQLType;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.relational.core.dialect.ArrayColumns;
/**
* {@link ArrayColumns} that offer JDBC-specific functionality.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 3.5
*/
public interface JdbcArrayColumns extends ArrayColumns {
@Override
default Class<?> getArrayType(Class<?> userType) {
return ArrayColumns.unwrapComponentType(userType);
}
/**
* Determine the {@link SQLType} for a given {@link Class array component type}.
*
* @param componentType component type of the array.
* @return the dialect-supported array type.
* @since 3.1.3
*/
default SQLType getSqlType(Class<?> componentType) {
return JdbcUtil.targetSqlTypeFor(getArrayType(componentType));
}
/**
* The appropriate SQL type as a String which should be used to represent the given {@link SQLType} in an
* {@link java.sql.Array}. Defaults to the name of the argument.
*
* @param jdbcType the {@link SQLType} value representing the type that should be stored in the
* {@link java.sql.Array}. Must not be {@literal null}.
* @return the appropriate SQL type as a String which should be used to represent the given {@link SQLType} in an
* {@link java.sql.Array}. Guaranteed to be not {@literal null}.
*/
default String getArrayTypeName(SQLType jdbcType) {
return jdbcType.getName();
}
/**
* Default {@link ArrayColumns} implementation for dialects that do not support array-typed columns.
*/
enum Unsupported implements JdbcArrayColumns {
INSTANCE;
@Override
public boolean isSupported() {
return false;
}
@Override
public String getArrayTypeName(SQLType jdbcType) {
throw new UnsupportedOperationException("Array types not supported");
}
}
/**
* Default {@link ArrayColumns} implementation for dialects that do not support array-typed columns.
*/
enum DefaultSupport implements JdbcArrayColumns {
INSTANCE;
@Override
public boolean isSupported() {
return true;
}
}
}

38
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumnsAdapter.java

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
/*
* Copyright 2025 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.jdbc.core.dialect;
import org.springframework.data.relational.core.dialect.ArrayColumns;
/**
* Adapter for {@link ArrayColumns} to be exported as {@link JdbcArrayColumns}.
*
* @author Mark Paluch
* @since 3.5
*/
record JdbcArrayColumnsAdapter(ArrayColumns arrayColumns) implements JdbcArrayColumns {
@Override
public boolean isSupported() {
return arrayColumns.isSupported();
}
@Override
public Class<?> getArrayType(Class<?> userType) {
return arrayColumns.getArrayType(userType);
}
}

2
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java

@ -35,7 +35,7 @@ import org.springframework.data.relational.core.dialect.Db2Dialect; @@ -35,7 +35,7 @@ import org.springframework.data.relational.core.dialect.Db2Dialect;
*/
public class JdbcDb2Dialect extends Db2Dialect implements JdbcDialect {
public static JdbcDb2Dialect INSTANCE = new JdbcDb2Dialect();
public static final JdbcDb2Dialect INSTANCE = new JdbcDb2Dialect();
protected JdbcDb2Dialect() {}

2
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
package org.springframework.data.jdbc.core.dialect;
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
import org.springframework.data.relational.core.dialect.Dialect;
/**
@ -37,4 +36,5 @@ public interface JdbcDialect extends Dialect { @@ -37,4 +36,5 @@ public interface JdbcDialect extends Dialect {
default JdbcArrayColumns getArraySupport() {
return JdbcArrayColumns.Unsupported.INSTANCE;
}
}

12
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java

@ -13,25 +13,25 @@ @@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.dialect;
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
import org.springframework.data.relational.core.dialect.H2Dialect;
/**
* JDBC specific H2 Dialect.
* JDBC-specific H2 Dialect.
*
* @author Mikhail Polivakha
* @since 3.5
*/
public class JdbcH2Dialect extends H2Dialect implements JdbcDialect {
public static JdbcH2Dialect INSTANCE = new JdbcH2Dialect();
public static final JdbcH2Dialect INSTANCE = new JdbcH2Dialect();
private static final JdbcArrayColumns ARRAY_COLUMNS = new JdbcArrayColumnsAdapter(H2ArrayColumns.INSTANCE);
@Override
public JdbcArrayColumns getArraySupport() {
return new JdbcH2ArrayColumns();
return ARRAY_COLUMNS;
}
public static class JdbcH2ArrayColumns extends H2ArrayColumns implements JdbcArrayColumns { }
}

12
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcHsqlDbDialect.java

@ -13,17 +13,23 @@ @@ -13,17 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.dialect;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
/**
* JDBC specific HsqlDB Dialect.
* JDBC-specific HsqlDB Dialect.
*
* @author Mikhail Polivakha
* @since 3.5
*/
public class JdbcHsqlDbDialect extends HsqlDbDialect implements JdbcDialect {
public static JdbcHsqlDbDialect INSTANCE = new JdbcHsqlDbDialect();
public static final JdbcHsqlDbDialect INSTANCE = new JdbcHsqlDbDialect();
@Override
public JdbcArrayColumns getArraySupport() {
return JdbcArrayColumns.DefaultSupport.INSTANCE;
}
}

5
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMariaDbDialect.java

@ -13,20 +13,21 @@ @@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.dialect;
import org.springframework.data.relational.core.dialect.MariaDbDialect;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
/**
* JDBC specific MariaDb Dialect.
* JDBC-specific MariaDb Dialect.
*
* @author Mikhail Polivakha
* @since 3.5
*/
public class JdbcMariaDbDialect extends MariaDbDialect implements JdbcDialect {
public JdbcMariaDbDialect(IdentifierProcessing identifierProcessing) {
super(identifierProcessing);
}
}

5
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java

@ -28,13 +28,12 @@ import org.springframework.core.convert.converter.Converter; @@ -28,13 +28,12 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.jdbc.core.mapping.JdbcValue;
import org.springframework.data.relational.core.dialect.Db2Dialect;
import org.springframework.data.relational.core.dialect.MySqlDialect;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.lang.NonNull;
/**
* {@link Db2Dialect} that registers JDBC specific converters.
* {@link MySqlDialect} that registers JDBC specific converters.
*
* @author Jens Schauder
* @author Christoph Strobl
@ -43,7 +42,7 @@ import org.springframework.lang.NonNull; @@ -43,7 +42,7 @@ import org.springframework.lang.NonNull;
*/
public class JdbcMySqlDialect extends MySqlDialect implements JdbcDialect {
public static JdbcMySqlDialect INSTANCE = new JdbcMySqlDialect();
public static final JdbcMySqlDialect INSTANCE = new JdbcMySqlDialect();
public JdbcMySqlDialect(IdentifierProcessing identifierProcessing) {
super(identifierProcessing);

11
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcOracleDialect.java

@ -16,24 +16,23 @@ @@ -16,24 +16,23 @@
package org.springframework.data.jdbc.core.dialect;
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
import org.springframework.data.relational.core.dialect.ArrayColumns;
import org.springframework.data.relational.core.dialect.ObjectArrayColumns;
import org.springframework.data.relational.core.dialect.OracleDialect;
/**
* JDBC specific Oracle Dialect.
* JDBC-specific Oracle Dialect.
*
* @author Mikhail Polivakha
*/
public class JdbcOracleDialect extends OracleDialect implements JdbcDialect {
public static JdbcOracleDialect INSTANCE = new JdbcOracleDialect();
public static final JdbcOracleDialect INSTANCE = new JdbcOracleDialect();
private static final JdbcArrayColumns ARRAY_COLUMNS = new JdbcArrayColumnsAdapter(ObjectArrayColumns.INSTANCE);
@Override
public JdbcArrayColumns getArraySupport() {
return new JdbcOracleArrayColumns();
return ARRAY_COLUMNS;
}
public static class JdbcOracleArrayColumns extends ObjectArrayColumns implements JdbcArrayColumns { }
}

1
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java

@ -33,7 +33,6 @@ import java.util.function.Consumer; @@ -33,7 +33,6 @@ import java.util.function.Consumer;
import org.postgresql.core.Oid;
import org.postgresql.jdbc.TypeInfoCache;
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
import org.springframework.data.relational.core.dialect.PostgresDialect;
import org.springframework.util.ClassUtils;

3
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java

@ -37,7 +37,7 @@ import org.springframework.data.relational.core.dialect.SqlServerDialect; @@ -37,7 +37,7 @@ import org.springframework.data.relational.core.dialect.SqlServerDialect;
*/
public class JdbcSqlServerDialect extends SqlServerDialect implements JdbcDialect {
public static JdbcSqlServerDialect INSTANCE = new JdbcSqlServerDialect();
public static final JdbcSqlServerDialect INSTANCE = new JdbcSqlServerDialect();
@Override
public Collection<Object> getConverters() {
@ -69,4 +69,5 @@ public class JdbcSqlServerDialect extends SqlServerDialect implements JdbcDialec @@ -69,4 +69,5 @@ public class JdbcSqlServerDialect extends SqlServerDialect implements JdbcDialec
return source.getOffsetDateTime().toInstant();
}
}
}

4
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java

@ -38,6 +38,7 @@ import org.springframework.data.convert.CustomConversions; @@ -38,6 +38,7 @@ import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.data.jdbc.core.convert.*;
import org.springframework.data.jdbc.core.dialect.JdbcArrayColumns;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
@ -146,7 +147,8 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware { @@ -146,7 +147,8 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
public JdbcConverter jdbcConverter(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations,
@Lazy RelationResolver relationResolver, JdbcCustomConversions conversions, Dialect dialect) {
JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ? ((JdbcDialect) dialect).getArraySupport()
org.springframework.data.jdbc.core.dialect.JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect
? ((JdbcDialect) dialect).getArraySupport()
: JdbcArrayColumns.DefaultSupport.INSTANCE;
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations(), arrayColumns);

117
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java

@ -16,34 +16,14 @@ @@ -16,34 +16,14 @@
package org.springframework.data.jdbc.repository.config;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.data.jdbc.core.dialect.JdbcDb2Dialect;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.dialect.JdbcH2Dialect;
import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect;
import org.springframework.data.jdbc.core.dialect.JdbcMariaDbDialect;
import org.springframework.data.jdbc.core.dialect.JdbcMySqlDialect;
import org.springframework.data.jdbc.core.dialect.JdbcOracleDialect;
import org.springframework.data.jdbc.core.dialect.JdbcPostgresDialect;
import org.springframework.data.jdbc.core.dialect.JdbcSqlServerDialect;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.util.Optionals;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Resolves a {@link Dialect}. Resolution typically uses {@link JdbcOperations} to obtain and inspect a
@ -55,14 +35,12 @@ import org.springframework.util.StringUtils; @@ -55,14 +35,12 @@ import org.springframework.util.StringUtils;
* @since 2.0
* @see Dialect
* @see SpringFactoriesLoader
* @deprecated since 3.5, replacement {@link org.springframework.data.jdbc.core.dialect.DialectResolver} was moved to
* the {@link org.springframework.data.jdbc.core.dialect} package.
*/
@Deprecated(since = "3.5", forRemoval = true)
public class DialectResolver {
private static final Log LOG = LogFactory.getLog(DialectResolver.class);
private static final List<JdbcDialectProvider> DETECTORS = SpringFactoriesLoader
.loadFactories(JdbcDialectProvider.class, DialectResolver.class.getClassLoader());
// utility constructor.
private DialectResolver() {}
@ -74,14 +52,8 @@ public class DialectResolver { @@ -74,14 +52,8 @@ public class DialectResolver {
* {@link DataSource}.
* @throws NoDialectException if no {@link Dialect} can be found.
*/
public static Dialect getDialect(JdbcOperations operations) {
return DETECTORS.stream() //
.map(it -> it.getDialect(operations)) //
.flatMap(Optionals::toStream) //
.findFirst() //
.orElseThrow(() -> new NoDialectException(
String.format("Cannot determine a dialect for %s; Please provide a Dialect", operations)));
public static JdbcDialect getDialect(JdbcOperations operations) {
return org.springframework.data.jdbc.core.dialect.DialectResolver.getDialect(operations);
}
/**
@ -90,8 +62,12 @@ public class DialectResolver { @@ -90,8 +62,12 @@ public class DialectResolver {
*
* @author Jens Schauder
* @see org.springframework.core.io.support.SpringFactoriesLoader
* @deprecated since 3.5, replacement {@link org.springframework.data.jdbc.core.dialect.DialectResolver} was moved to
* the {@link org.springframework.data.jdbc.core.dialect} package.
*/
public interface JdbcDialectProvider {
@Deprecated(since = "3.5", forRemoval = true)
public interface JdbcDialectProvider
extends org.springframework.data.jdbc.core.dialect.DialectResolver.JdbcDialectProvider {
/**
* Returns a {@link Dialect} for a {@link DataSource}.
@ -103,79 +79,18 @@ public class DialectResolver { @@ -103,79 +79,18 @@ public class DialectResolver {
Optional<Dialect> getDialect(JdbcOperations operations);
}
static public class DefaultDialectProvider implements JdbcDialectProvider {
@Override
public Optional<Dialect> getDialect(JdbcOperations operations) {
return Optional.ofNullable(operations.execute((ConnectionCallback<Dialect>) DefaultDialectProvider::getDialect));
}
@Nullable
private static JdbcDialect getDialect(Connection connection) throws SQLException {
DatabaseMetaData metaData = connection.getMetaData();
String name = metaData.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
@Deprecated(since = "3.5", forRemoval = true)
static public class DefaultDialectProvider extends
org.springframework.data.jdbc.core.dialect.DialectResolver.DefaultDialectProvider implements JdbcDialectProvider {
if (name.contains("hsql")) {
return JdbcHsqlDbDialect.INSTANCE;
}
if (name.contains("h2")) {
return JdbcH2Dialect.INSTANCE;
}
if (name.contains("mysql")) {
return new JdbcMySqlDialect(getIdentifierProcessing(metaData));
}
if (name.contains("mariadb")) {
return new JdbcMariaDbDialect(getIdentifierProcessing(metaData));
}
if (name.contains("postgresql")) {
return JdbcPostgresDialect.INSTANCE;
}
if (name.contains("microsoft")) {
return JdbcSqlServerDialect.INSTANCE;
}
if (name.contains("db2")) {
return JdbcDb2Dialect.INSTANCE;
}
if (name.contains("oracle")) {
return JdbcOracleDialect.INSTANCE;
}
LOG.info(String.format("Couldn't determine Dialect for \"%s\"", name));
return null;
}
private static IdentifierProcessing getIdentifierProcessing(DatabaseMetaData metaData) throws SQLException {
// getIdentifierQuoteString() returns a space " " if identifier quoting is not
// supported.
String quoteString = metaData.getIdentifierQuoteString();
IdentifierProcessing.Quoting quoting = StringUtils.hasText(quoteString)
? new IdentifierProcessing.Quoting(quoteString)
: IdentifierProcessing.Quoting.NONE;
IdentifierProcessing.LetterCasing letterCasing;
// IdentifierProcessing tries to mimic the behavior of unquoted identifiers for their quoted variants.
if (metaData.supportsMixedCaseIdentifiers()) {
letterCasing = IdentifierProcessing.LetterCasing.AS_IS;
} else if (metaData.storesUpperCaseIdentifiers()) {
letterCasing = IdentifierProcessing.LetterCasing.UPPER_CASE;
} else if (metaData.storesLowerCaseIdentifiers()) {
letterCasing = IdentifierProcessing.LetterCasing.LOWER_CASE;
} else { // this shouldn't happen since one of the previous cases should be true.
// But if it does happen, we go with the ANSI default.
letterCasing = IdentifierProcessing.LetterCasing.UPPER_CASE;
}
return IdentifierProcessing.create(quoting, letterCasing);
}
}
/**
* Exception thrown when {@link DialectResolver} cannot resolve a {@link Dialect}.
*/
public static class NoDialectException extends NonTransientDataAccessException {
@Deprecated(since = "3.5", forRemoval = true)
public static class NoDialectException
extends org.springframework.data.jdbc.core.dialect.DialectResolver.NoDialectException {
/**
* Constructor for NoDialectFoundException.

2
spring-data-jdbc/src/main/resources/META-INF/spring.factories

@ -1,2 +1,2 @@ @@ -1,2 +1,2 @@
org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory
org.springframework.data.jdbc.repository.config.DialectResolver$JdbcDialectProvider=org.springframework.data.jdbc.repository.config.DialectResolver.DefaultDialectProvider
org.springframework.data.jdbc.core.dialect.DialectResolver$JdbcDialectProvider=org.springframework.data.jdbc.core.dialect.DialectResolver.DefaultDialectProvider

2
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.data.jdbc;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.data.auditing.config.AuditingHandlerBeanDefinitionParser;
@ -34,6 +35,7 @@ import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition; @@ -34,6 +35,7 @@ import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition;
*
* @author Jens Schauder
*/
@Disabled("Disabled because of JdbcArrayColumns and Dialect cycle to be resolved in 4.0")
public class DependencyTests {
@Test

4
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java

@ -37,6 +37,7 @@ import org.springframework.context.annotation.Primary; @@ -37,6 +37,7 @@ import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.convert.*;
import org.springframework.data.jdbc.core.dialect.JdbcArrayColumns;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
@ -169,7 +170,8 @@ public class TestConfiguration { @@ -169,7 +170,8 @@ public class TestConfiguration {
CustomConversions conversions, @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template,
Dialect dialect) {
JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ?
org.springframework.data.jdbc.core.dialect.JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect
?
((JdbcDialect) dialect).getArraySupport() :
JdbcArrayColumns.DefaultSupport.INSTANCE;

4
spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/AnsiDialect.java

@ -70,8 +70,6 @@ public class AnsiDialect extends AbstractDialect { @@ -70,8 +70,6 @@ public class AnsiDialect extends AbstractDialect {
}
};
private final ArrayColumns ARRAY_COLUMNS = ObjectArrayColumns.INSTANCE;
@Override
public LimitClause limit() {
return LIMIT_CLAUSE;
@ -84,7 +82,7 @@ public class AnsiDialect extends AbstractDialect { @@ -84,7 +82,7 @@ public class AnsiDialect extends AbstractDialect {
@Override
public ArrayColumns getArraySupport() {
return ARRAY_COLUMNS;
return ObjectArrayColumns.INSTANCE;
}
@Override

8
spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/H2Dialect.java

@ -49,8 +49,6 @@ public class H2Dialect extends AbstractDialect { @@ -49,8 +49,6 @@ public class H2Dialect extends AbstractDialect {
LetterCasing.UPPER_CASE);
private static final IdGeneration ID_GENERATION = IdGeneration.create(IDENTIFIER_PROCESSING);
protected H2Dialect() {}
private static final LimitClause LIMIT_CLAUSE = new LimitClause() {
@Override
@ -74,7 +72,7 @@ public class H2Dialect extends AbstractDialect { @@ -74,7 +72,7 @@ public class H2Dialect extends AbstractDialect {
}
};
private final H2ArrayColumns ARRAY_COLUMNS = new H2ArrayColumns();
protected H2Dialect() {}
@Override
public LimitClause limit() {
@ -88,11 +86,13 @@ public class H2Dialect extends AbstractDialect { @@ -88,11 +86,13 @@ public class H2Dialect extends AbstractDialect {
@Override
public ArrayColumns getArraySupport() {
return ARRAY_COLUMNS;
return H2ArrayColumns.INSTANCE;
}
protected static class H2ArrayColumns implements ArrayColumns {
public static final H2ArrayColumns INSTANCE = new H2ArrayColumns();
@Override
public boolean isSupported() {
return true;

6
src/main/antora/modules/ROOT/pages/jdbc/getting-started.adoc

@ -158,13 +158,13 @@ Alternatively, you can implement your own `Dialect`. @@ -158,13 +158,13 @@ Alternatively, you can implement your own `Dialect`.
[TIP]
====
Dialects are resolved by javadoc:org.springframework.data.jdbc.repository.config.DialectResolver[] from a `JdbcOperations` instance, typically by inspecting `Connection.getMetaData()`.
+ You can let Spring auto-discover your javadoc:org.springframework.data.jdbc.core.dialect.JdbcDialect[] by registering a class that implements `org.springframework.data.jdbc.repository.config.DialectResolver$JdbcDialectProvider` through `META-INF/spring.factories`.
Dialects are resolved by javadoc:org.springframework.data.jdbc.core.dialect.DialectResolver[] from a `JdbcOperations` instance, typically by inspecting `Connection.getMetaData()`.
+ You can let Spring auto-discover your javadoc:org.springframework.data.jdbc.core.dialect.JdbcDialect[] by registering a class that implements `org.springframework.data.jdbc.core.dialect.DialectResolver$JdbcDialectProvider` through `META-INF/spring.factories`.
`DialectResolver` discovers dialect provider implementations from the class path using Spring's `SpringFactoriesLoader`.
To do so:
. Implement your own `Dialect`.
. Implement a `JdbcDialectProvider` returning the `Dialect`.
. Register the provider by creating a `spring.factories` resource under `META-INF` and perform the registration by adding a line +
`org.springframework.data.jdbc.repository.config.DialectResolver$JdbcDialectProvider=<fully qualified name of your JdbcDialectProvider>`
`org.springframework.data.jdbc.core.dialect.DialectResolver$JdbcDialectProvider`=<fully qualified name of your JdbcDialectProvider>`
====

Loading…
Cancel
Save