diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java index a031e79fd8..040367b06a 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java @@ -33,7 +33,7 @@ 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 { * @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 DefaultJdbcTypeFactory(JdbcOperations operations) { * * @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 DefaultJdbcTypeFactory(JdbcOperations operations, JdbcArrayColumns arrayC 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) { 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 e3df13ddc7..5f68fbb735 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 @@ -26,8 +26,10 @@ * @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) { @@ -88,12 +90,5 @@ enum DefaultSupport implements JdbcArrayColumns { public boolean isSupported() { return true; } - - @Override - public String getArrayTypeName(SQLType jdbcType) { - return jdbcType.getName(); - } - } - } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java new file mode 100644 index 0000000000..21d1433d82 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java @@ -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 DETECTORS = SpringFactoriesLoader + .loadFactories(JdbcDialectProvider.class, DialectResolver.class.getClassLoader()); + + private static final List 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 getDialect(JdbcOperations operations); + } + + public static class DefaultDialectProvider implements JdbcDialectProvider { + + @Override + public Optional getDialect(JdbcOperations operations) { + return Optional.ofNullable(operations.execute((ConnectionCallback) 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 getConverters() { + return delegate.getConverters(); + } + + @Override + public Set> 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(); + } + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumns.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumns.java new file mode 100644 index 0000000000..60568a7ee0 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumns.java @@ -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; + } + } +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumnsAdapter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumnsAdapter.java new file mode 100644 index 0000000000..6a117a2d5f --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcArrayColumnsAdapter.java @@ -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); + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java index 86027b9ca7..2288a44c18 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java @@ -33,9 +33,9 @@ * @author Christoph Strobl * @since 2.3 */ -public class JdbcDb2Dialect extends Db2Dialect { +public class JdbcDb2Dialect extends Db2Dialect implements JdbcDialect { - public static JdbcDb2Dialect INSTANCE = new JdbcDb2Dialect(); + public static final JdbcDb2Dialect INSTANCE = new JdbcDb2Dialect(); protected JdbcDb2Dialect() {} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java index d20b935700..5728ce4f56 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java @@ -15,13 +15,13 @@ */ package org.springframework.data.jdbc.core.dialect; -import org.springframework.data.jdbc.core.convert.JdbcArrayColumns; import org.springframework.data.relational.core.dialect.Dialect; /** * {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC specific functionality. * * @author Jens Schauder + * @author Mikhail Polivakha * @since 2.3 */ public interface JdbcDialect extends Dialect { @@ -33,6 +33,8 @@ public interface JdbcDialect extends Dialect { * @return the JDBC specific array support object that describes how array-typed columns are supported by this * dialect. */ - @Override - JdbcArrayColumns getArraySupport(); + default JdbcArrayColumns getArraySupport() { + return JdbcArrayColumns.Unsupported.INSTANCE; + } + } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java new file mode 100644 index 0000000000..8f781ef9db --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java @@ -0,0 +1,37 @@ +/* + * 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.H2Dialect; + +/** + * JDBC-specific H2 Dialect. + * + * @author Mikhail Polivakha + * @since 3.5 + */ +public class JdbcH2Dialect extends H2Dialect implements JdbcDialect { + + public static final JdbcH2Dialect INSTANCE = new JdbcH2Dialect(); + + private static final JdbcArrayColumns ARRAY_COLUMNS = new JdbcArrayColumnsAdapter(H2ArrayColumns.INSTANCE); + + @Override + public JdbcArrayColumns getArraySupport() { + return ARRAY_COLUMNS; + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcHsqlDbDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcHsqlDbDialect.java new file mode 100644 index 0000000000..77f7531edc --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcHsqlDbDialect.java @@ -0,0 +1,35 @@ +/* + * 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.HsqlDbDialect; + +/** + * JDBC-specific HsqlDB Dialect. + * + * @author Mikhail Polivakha + * @since 3.5 + */ +public class JdbcHsqlDbDialect extends HsqlDbDialect implements JdbcDialect { + + public static final JdbcHsqlDbDialect INSTANCE = new JdbcHsqlDbDialect(); + + @Override + public JdbcArrayColumns getArraySupport() { + return JdbcArrayColumns.DefaultSupport.INSTANCE; + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMariaDbDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMariaDbDialect.java new file mode 100644 index 0000000000..16c416f736 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMariaDbDialect.java @@ -0,0 +1,33 @@ +/* + * 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.MariaDbDialect; +import org.springframework.data.relational.core.sql.IdentifierProcessing; + +/** + * JDBC-specific MariaDb Dialect. + * + * @author Mikhail Polivakha + * @since 3.5 + */ +public class JdbcMariaDbDialect extends MariaDbDialect implements JdbcDialect { + + public JdbcMariaDbDialect(IdentifierProcessing identifierProcessing) { + super(identifierProcessing); + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java index 4295d60e0c..76079db6a4 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java @@ -28,19 +28,21 @@ 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 + * @author Mikhail Polivakha * @since 2.3 */ -public class JdbcMySqlDialect extends MySqlDialect { +public class JdbcMySqlDialect extends MySqlDialect implements JdbcDialect { + + public static final JdbcMySqlDialect INSTANCE = new JdbcMySqlDialect(); public JdbcMySqlDialect(IdentifierProcessing identifierProcessing) { super(identifierProcessing); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcOracleDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcOracleDialect.java new file mode 100644 index 0000000000..3b0b40cce9 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcOracleDialect.java @@ -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.ObjectArrayColumns; +import org.springframework.data.relational.core.dialect.OracleDialect; + +/** + * JDBC-specific Oracle Dialect. + * + * @author Mikhail Polivakha + */ +public class JdbcOracleDialect extends OracleDialect implements JdbcDialect { + + public static final JdbcOracleDialect INSTANCE = new JdbcOracleDialect(); + + private static final JdbcArrayColumns ARRAY_COLUMNS = new JdbcArrayColumnsAdapter(ObjectArrayColumns.INSTANCE); + + @Override + public JdbcArrayColumns getArraySupport() { + return ARRAY_COLUMNS; + } + +} 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 24f5a69ae7..b2c9b91626 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 @@ -33,7 +33,6 @@ 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; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java index e147e841d2..bc45ad3dda 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java @@ -35,9 +35,9 @@ * @author Mikhail Polivakha * @since 2.3 */ -public class JdbcSqlServerDialect extends SqlServerDialect { +public class JdbcSqlServerDialect extends SqlServerDialect implements JdbcDialect { - public static JdbcSqlServerDialect INSTANCE = new JdbcSqlServerDialect(); + public static final JdbcSqlServerDialect INSTANCE = new JdbcSqlServerDialect(); @Override public Collection getConverters() { @@ -69,4 +69,5 @@ public Instant convert(DateTimeOffset source) { return source.getOffsetDateTime().toInstant(); } } + } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java index ac4483069e..af7c3352e1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java @@ -38,6 +38,7 @@ 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 IdGeneratingEntityCallback idGeneratingBeforeSaveCallback(JdbcMappingCont 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); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java index 99eb15cf61..1f81381741 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java @@ -16,33 +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.JdbcMySqlDialect; -import org.springframework.data.jdbc.core.dialect.JdbcPostgresDialect; -import org.springframework.data.jdbc.core.dialect.JdbcSqlServerDialect; +import org.springframework.data.jdbc.core.dialect.JdbcDialect; import org.springframework.data.relational.core.dialect.Dialect; -import org.springframework.data.relational.core.dialect.H2Dialect; -import org.springframework.data.relational.core.dialect.HsqlDbDialect; -import org.springframework.data.relational.core.dialect.MariaDbDialect; -import org.springframework.data.relational.core.dialect.OracleDialect; -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 @@ -50,17 +31,16 @@ * available {@link JdbcDialectProvider extensions}. * * @author Jens Schauder + * @author Mikhail Polivakha * @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 DETECTORS = SpringFactoriesLoader - .loadFactories(JdbcDialectProvider.class, DialectResolver.class.getClassLoader()); - // utility constructor. private DialectResolver() {} @@ -72,14 +52,8 @@ private 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); } /** @@ -88,8 +62,12 @@ public static Dialect getDialect(JdbcOperations operations) { * * @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}. @@ -101,79 +79,18 @@ public interface JdbcDialectProvider { Optional getDialect(JdbcOperations operations); } - static public class DefaultDialectProvider implements JdbcDialectProvider { - - @Override - public Optional getDialect(JdbcOperations operations) { - return Optional.ofNullable(operations.execute((ConnectionCallback) DefaultDialectProvider::getDialect)); - } - - @Nullable - private static Dialect 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 HsqlDbDialect.INSTANCE; - } - if (name.contains("h2")) { - return H2Dialect.INSTANCE; - } - if (name.contains("mysql")) { - return new JdbcMySqlDialect(getIdentifierProcessing(metaData)); - } - if (name.contains("mariadb")) { - return new MariaDbDialect(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 OracleDialect.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. diff --git a/spring-data-jdbc/src/main/resources/META-INF/spring.factories b/spring-data-jdbc/src/main/resources/META-INF/spring.factories index cc0d5cce5e..dedc6fdf90 100644 --- a/spring-data-jdbc/src/main/resources/META-INF/spring.factories +++ b/spring-data-jdbc/src/main/resources/META-INF/spring.factories @@ -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 diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java index b754581659..d7d142b4a8 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java @@ -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 @@ * * @author Jens Schauder */ +@Disabled("Disabled because of JdbcArrayColumns and Dialect cycle to be resolved in 4.0") public class DependencyTests { @Test diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java index 9f3bd72fd7..99eb539870 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; +import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.relational.core.conversion.IdValueSource; import org.springframework.data.relational.core.dialect.Dialect; @@ -58,7 +59,7 @@ class DefaultDataAccessStrategyUnitTests { void before() { DelegatingDataAccessStrategy relationResolver = new DelegatingDataAccessStrategy(); - Dialect dialect = HsqlDbDialect.INSTANCE; + Dialect dialect = JdbcHsqlDbDialect.INSTANCE; converter = new MappingJdbcConverter(context, relationResolver, new JdbcCustomConversions(), new DefaultJdbcTypeFactory(jdbcOperations)); accessStrategy = new DataAccessStrategyFactory( // diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallbackTest.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallbackTest.java index 240d47f596..d2932931f0 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallbackTest.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdGeneratingEntityCallbackTest.java @@ -15,10 +15,11 @@ */ package org.springframework.data.jdbc.core.convert; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.UUID; @@ -27,12 +28,11 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.data.annotation.Id; +import org.springframework.data.jdbc.core.dialect.JdbcMySqlDialect; +import org.springframework.data.jdbc.core.dialect.JdbcPostgresDialect; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.relational.core.conversion.MutableAggregateChange; -import org.springframework.data.relational.core.dialect.MySqlDialect; -import org.springframework.data.relational.core.dialect.PostgresDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.Sequence; import org.springframework.data.relational.core.mapping.Table; @@ -56,7 +56,7 @@ class IdGeneratingEntityCallbackTest { void setUp() { relationalMappingContext = new RelationalMappingContext(); - relationalMappingContext.setSimpleTypeHolder(new SimpleTypeHolder(PostgresDialect.INSTANCE.simpleTypes(), true)); + relationalMappingContext.setSimpleTypeHolder(new SimpleTypeHolder(JdbcPostgresDialect.INSTANCE.simpleTypes(), true)); } @Test // GH-1923 @@ -65,7 +65,7 @@ void sequenceGenerationIsNotSupported() { NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class); IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, - MySqlDialect.INSTANCE, operations); + JdbcMySqlDialect.INSTANCE, operations); EntityWithSequence processed = (EntityWithSequence) subject.onBeforeSave(new EntityWithSequence(), MutableAggregateChange.forSave(new EntityWithSequence())); @@ -77,7 +77,7 @@ void sequenceGenerationIsNotSupported() { void entityIsNotMarkedWithTargetSequence() { IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, - MySqlDialect.INSTANCE, operations); + JdbcMySqlDialect.INSTANCE, operations); NoSequenceEntity processed = (NoSequenceEntity) subject.onBeforeSave(new NoSequenceEntity(), MutableAggregateChange.forSave(new NoSequenceEntity())); @@ -93,7 +93,7 @@ void entityIdIsPopulatedFromSequence() { .thenReturn(generatedId); IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, - PostgresDialect.INSTANCE, operations); + JdbcPostgresDialect.INSTANCE, operations); EntityWithSequence processed = (EntityWithSequence) subject.onBeforeSave(new EntityWithSequence(), MutableAggregateChange.forSave(new EntityWithSequence())); @@ -109,7 +109,7 @@ void appliesIntegerConversion() { .thenReturn(generatedId); IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, - PostgresDialect.INSTANCE, operations); + JdbcPostgresDialect.INSTANCE, operations); EntityWithIntSequence processed = (EntityWithIntSequence) subject.onBeforeSave(new EntityWithIntSequence(), MutableAggregateChange.forSave(new EntityWithIntSequence())); @@ -125,7 +125,7 @@ void assignsUuidValues() { .thenReturn(generatedId); IdGeneratingEntityCallback subject = new IdGeneratingEntityCallback(relationalMappingContext, - PostgresDialect.INSTANCE, operations); + JdbcPostgresDialect.INSTANCE, operations); EntityWithUuidSequence processed = (EntityWithUuidSequence) subject.onBeforeSave(new EntityWithUuidSequence(), MutableAggregateChange.forSave(new EntityWithUuidSequence())); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/NonQuotingDialect.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/NonQuotingDialect.java index edf150ab16..fda4f5d932 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/NonQuotingDialect.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/NonQuotingDialect.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jdbc.core.convert; +import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect; import org.springframework.data.relational.core.dialect.AbstractDialect; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.HsqlDbDialect; @@ -38,12 +39,12 @@ private NonQuotingDialect() {} @Override public LimitClause limit() { - return HsqlDbDialect.INSTANCE.limit(); + return JdbcHsqlDbDialect.INSTANCE.limit(); } @Override public LockClause lock() { - return HsqlDbDialect.INSTANCE.lock(); + return JdbcHsqlDbDialect.INSTANCE.lock(); } @Override diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java index d095b27ccb..cc264cbe62 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java @@ -15,11 +15,17 @@ */ package org.springframework.data.jdbc.core.convert; -import static java.util.Collections.*; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.SoftAssertions.*; -import static org.springframework.data.relational.core.mapping.ForeignKeyNaming.*; -import static org.springframework.data.relational.core.sql.SqlIdentifier.*; +import static java.util.Collections.emptySet; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.entry; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.springframework.data.relational.core.mapping.ForeignKeyNaming.APPLY_RENAMING; +import static org.springframework.data.relational.core.mapping.ForeignKeyNaming.IGNORE_RENAMING; +import static org.springframework.data.relational.core.sql.SqlIdentifier.EMPTY; +import static org.springframework.data.relational.core.sql.SqlIdentifier.quoted; +import static org.springframework.data.relational.core.sql.SqlIdentifier.unquoted; import java.util.Map; import java.util.Set; @@ -33,13 +39,13 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.PersistentPropertyPathTestUtils; +import org.springframework.data.jdbc.core.dialect.JdbcPostgresDialect; +import org.springframework.data.jdbc.core.dialect.JdbcSqlServerDialect; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.dialect.AnsiDialect; import org.springframework.data.relational.core.dialect.Dialect; -import org.springframework.data.relational.core.dialect.PostgresDialect; -import org.springframework.data.relational.core.dialect.SqlServerDialect; import org.springframework.data.relational.core.mapping.AggregatePath; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.DefaultNamingStrategy; @@ -274,7 +280,7 @@ void findAllSortedByMultipleFields() { @Test // GH-821 void findAllSortedWithNullHandling_resolvesNullHandlingWhenDialectSupportsIt() { - SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, PostgresDialect.INSTANCE); + SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, JdbcPostgresDialect.INSTANCE); String sql = sqlGenerator .getFindAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "name", Sort.NullHandling.NULLS_LAST))); @@ -285,7 +291,7 @@ void findAllSortedWithNullHandling_resolvesNullHandlingWhenDialectSupportsIt() { @Test // GH-821 void findAllSortedWithNullHandling_ignoresNullHandlingWhenDialectDoesNotSupportIt() { - SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, SqlServerDialect.INSTANCE); + SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, JdbcSqlServerDialect.INSTANCE); String sql = sqlGenerator .getFindAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "name", Sort.NullHandling.NULLS_LAST))); @@ -512,7 +518,7 @@ void updateWithVersion() { @Test // DATAJDBC-264 void getInsertForEmptyColumnListPostgres() { - SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, PostgresDialect.INSTANCE); + SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, JdbcPostgresDialect.INSTANCE); String insert = sqlGenerator.getInsert(emptySet()); @@ -522,7 +528,7 @@ void getInsertForEmptyColumnListPostgres() { @Test // GH-777 void gerInsertForEmptyColumnListMsSqlServer() { - SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, SqlServerDialect.INSTANCE); + SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, JdbcSqlServerDialect.INSTANCE); String insert = sqlGenerator.getInsert(emptySet()); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java index 5026385e8d..c5dd57d8e0 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java @@ -15,9 +15,7 @@ */ package org.springframework.data.jdbc.mybatis; -import static org.assertj.core.api.Assertions.*; - -import junit.framework.AssertionFailedError; +import static org.assertj.core.api.Assertions.assertThat; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; @@ -31,18 +29,20 @@ import org.springframework.context.annotation.Primary; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.testing.DatabaseType; import org.springframework.data.jdbc.testing.EnabledOnDatabase; import org.springframework.data.jdbc.testing.IntegrationTest; import org.springframework.data.jdbc.testing.TestClass; import org.springframework.data.jdbc.testing.TestConfiguration; -import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import junit.framework.AssertionFailedError; + /** * Tests the integration with Mybatis. * @@ -119,7 +119,7 @@ DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, JdbcConv SqlSession sqlSession, EmbeddedDatabase db) { return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, converter, - new NamedParameterJdbcTemplate(db), sqlSession, HsqlDbDialect.INSTANCE); + new NamedParameterJdbcTemplate(db), sqlSession, JdbcHsqlDbDialect.INSTANCE); } } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java index 8c306fea03..2670ed88be 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java @@ -16,8 +16,11 @@ package org.springframework.data.jdbc.repository; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; @@ -30,11 +33,11 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.jdbc.core.convert.MappingJdbcConverter; +import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.relational.core.dialect.Dialect; -import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.repository.CrudRepository; @@ -93,7 +96,7 @@ private String query() { private @NotNull T repository(Class repositoryInterface) { - Dialect dialect = HsqlDbDialect.INSTANCE; + Dialect dialect = JdbcHsqlDbDialect.INSTANCE; RelationalMappingContext context = new JdbcMappingContext(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java index 975c354cb5..e15ce6b68f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java @@ -15,11 +15,16 @@ */ package org.springframework.data.jdbc.repository; -import static java.util.Arrays.*; -import static org.assertj.core.api.Assertions.*; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.HashMap; @@ -33,13 +38,21 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.jdbc.core.convert.*; +import org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy; +import org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory; +import org.springframework.data.jdbc.core.convert.DelegatingDataAccessStrategy; +import org.springframework.data.jdbc.core.convert.InsertStrategyFactory; +import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; +import org.springframework.data.jdbc.core.convert.MappingJdbcConverter; +import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; +import org.springframework.data.jdbc.core.convert.SqlParametersFactory; +import org.springframework.data.jdbc.core.dialect.JdbcH2Dialect; +import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; import org.springframework.data.relational.core.dialect.Dialect; -import org.springframework.data.relational.core.dialect.H2Dialect; -import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.event.AfterConvertEvent; import org.springframework.data.relational.core.mapping.event.AfterDeleteEvent; @@ -86,7 +99,7 @@ void before() { RelationalMappingContext context = new JdbcMappingContext(); NamedParameterJdbcOperations operations = createIdGeneratingOperations(); - Dialect dialect = HsqlDbDialect.INSTANCE; + Dialect dialect = JdbcHsqlDbDialect.INSTANCE; DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy(); JdbcConverter converter = new MappingJdbcConverter(context, delegatingDataAccessStrategy, new JdbcCustomConversions(), new DefaultJdbcTypeFactory(operations.getJdbcOperations())); @@ -100,7 +113,7 @@ void before() { doReturn(true).when(dataAccessStrategy).update(any(), any()); JdbcRepositoryFactory factory = new JdbcRepositoryFactory(dataAccessStrategy, context, converter, - H2Dialect.INSTANCE, publisher, operations); + JdbcH2Dialect.INSTANCE, publisher, operations); this.repository = factory.getRepository(DummyEntityRepository.class); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfigurationIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfigurationIntegrationTests.java index 4638726a0d..b0ad7a4b1a 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfigurationIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfigurationIntegrationTests.java @@ -26,6 +26,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.jdbc.core.convert.CascadingDataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; +import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect; import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.HsqlDbDialect; @@ -70,7 +71,7 @@ public static class MyBatisJdbcConfigurationUnderTest extends MyBatisJdbcConfigu @Override @Bean public Dialect jdbcDialect(NamedParameterJdbcOperations operations) { - return HsqlDbDialect.INSTANCE; + return JdbcHsqlDbDialect.INSTANCE; } } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java index 27f4a47c29..a941d1830c 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java @@ -33,6 +33,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.MappingJdbcConverter; import org.springframework.data.jdbc.core.convert.RelationResolver; +import org.springframework.data.jdbc.core.dialect.JdbcH2Dialect; import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -669,7 +670,7 @@ public void createsQueryForCountProjection() throws Exception { } private PartTreeJdbcQuery createQuery(JdbcQueryMethod queryMethod) { - return new PartTreeJdbcQuery(mappingContext, queryMethod, H2Dialect.INSTANCE, converter, + return new PartTreeJdbcQuery(mappingContext, queryMethod, JdbcH2Dialect.INSTANCE, converter, mock(NamedParameterJdbcOperations.class), mock(RowMapper.class)); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java index 7b70956890..80ec8594b4 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategyUnitTests.java @@ -30,6 +30,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.core.dialect.JdbcH2Dialect; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.config.DefaultQueryMappingConfiguration; import org.springframework.data.jdbc.repository.query.Query; @@ -138,7 +139,7 @@ void correctLookUpStrategyForKey(QueryLookupStrategy.Key key, Class expectedClas .registerRowMapper(NumberFormat.class, numberFormatMapper); QueryLookupStrategy queryLookupStrategy = JdbcQueryLookupStrategy.create(key, publisher, callbacks, mappingContext, - converter, H2Dialect.INSTANCE, mappingConfiguration, operations, null, ValueExpressionDelegate.create()); + converter, JdbcH2Dialect.INSTANCE, mappingConfiguration, operations, null, ValueExpressionDelegate.create()); assertThat(queryLookupStrategy).isInstanceOf(expectedClass); } @@ -158,7 +159,7 @@ private RepositoryQuery getRepositoryQuery(QueryLookupStrategy.Key key, String n QueryMappingConfiguration mappingConfiguration) { QueryLookupStrategy queryLookupStrategy = JdbcQueryLookupStrategy.create(key, publisher, callbacks, mappingContext, - converter, H2Dialect.INSTANCE, mappingConfiguration, operations, null, ValueExpressionDelegate.create()); + converter, JdbcH2Dialect.INSTANCE, mappingConfiguration, operations, null, ValueExpressionDelegate.create()); Method method = ReflectionUtils.findMethod(MyRepository.class, name); return queryLookupStrategy.resolveQuery(method, metadata, projectionFactory, namedQueries); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java index ea3e5482cf..0767c2ee73 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java @@ -37,6 +37,7 @@ 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 @@ JdbcConverter relationalConverter(RelationalMappingContext mappingContext, @Lazy 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; 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 044d0f62b0..f9c713bc47 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 @@ -70,8 +70,6 @@ public Position getClausePosition() { } }; - private final ArrayColumns ARRAY_COLUMNS = ObjectArrayColumns.INSTANCE; - @Override public LimitClause limit() { return LIMIT_CLAUSE; @@ -84,7 +82,7 @@ public LockClause lock() { @Override public ArrayColumns getArraySupport() { - return ARRAY_COLUMNS; + return ObjectArrayColumns.INSTANCE; } @Override diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Db2Dialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Db2Dialect.java index 726016cfbb..4f21291a12 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Db2Dialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Db2Dialect.java @@ -32,7 +32,10 @@ public class Db2Dialect extends AbstractDialect { /** * Singleton instance. + * + * @deprecated use the {@code org.springframework.data.jdbc.core.dialect.JdbcDb2Dialect} directly. */ + @Deprecated(forRemoval = true) public static final Db2Dialect INSTANCE = new Db2Dialect(); private static final IdGeneration ID_GENERATION = new IdGeneration() { @@ -43,6 +46,7 @@ public boolean supportedForBatchOperations() { @Override public String createSequenceQuery(SqlIdentifier sequenceName) { + /* * This workaround (non-ANSI SQL way of querying sequence) exists for the same reasons it exists for {@link HsqlDbDialect} * 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 aaab1cb745..24677ccbb6 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 @@ -38,14 +38,17 @@ public class H2Dialect extends AbstractDialect { /** * Singleton instance. + * + * @deprecated use either the {@code org.springframework.data.r2dbc.dialect.H2Dialect} or + * {@code org.springframework.data.jdbc.core.dialect.JdbcH2Dialect}. */ + @Deprecated(forRemoval = true) public static final H2Dialect INSTANCE = new H2Dialect(); + private static final IdentifierProcessing IDENTIFIER_PROCESSING = IdentifierProcessing.create(Quoting.ANSI, LetterCasing.UPPER_CASE); private static final IdGeneration ID_GENERATION = IdGeneration.create(IDENTIFIER_PROCESSING); - protected H2Dialect() {} - private static final LimitClause LIMIT_CLAUSE = new LimitClause() { @Override @@ -69,7 +72,7 @@ public Position getClausePosition() { } }; - private final H2ArrayColumns ARRAY_COLUMNS = new H2ArrayColumns(); + protected H2Dialect() {} @Override public LimitClause limit() { @@ -83,10 +86,12 @@ public LockClause lock() { @Override public ArrayColumns getArraySupport() { - return ARRAY_COLUMNS; + return H2ArrayColumns.INSTANCE; } - static class H2ArrayColumns implements ArrayColumns { + protected static class H2ArrayColumns implements ArrayColumns { + + public static final H2ArrayColumns INSTANCE = new H2ArrayColumns(); @Override public boolean isSupported() { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java index d893bffcf7..d3101168e9 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java @@ -26,6 +26,10 @@ */ public class HsqlDbDialect extends AbstractDialect { + /** + * @deprecated use the {@code org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect} directly. + */ + @Deprecated(forRemoval = true) public static final HsqlDbDialect INSTANCE = new HsqlDbDialect(); protected HsqlDbDialect() {} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java index bb3c3700d2..7d1c929fcc 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java @@ -43,7 +43,11 @@ public class MySqlDialect extends AbstractDialect { /** * Singleton instance. + * + * @deprecated use either the {@code org.springframework.data.r2dbc.dialect.MySqlDialect} or + * {@code org.springframework.data.jdbc.core.dialect.JdbcMySqlDialect} */ + @Deprecated(forRemoval = true) public static final MySqlDialect INSTANCE = new MySqlDialect(); private final IdentifierProcessing identifierProcessing; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java index 7f65461093..371ed852bb 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java @@ -15,7 +15,7 @@ */ package org.springframework.data.relational.core.dialect; -import static java.util.Arrays.*; +import static java.util.Arrays.asList; import java.util.Collection; @@ -35,7 +35,11 @@ public class OracleDialect extends AnsiDialect { /** * Singleton instance. + * + * @deprecated use either the {@code org.springframework.data.r2dbc.dialect.OracleDialect} or + * {@code org.springframework.data.jdbc.core.dialect.JdbcOracleDialect}. */ + @Deprecated(forRemoval = true) public static final OracleDialect INSTANCE = new OracleDialect(); private static final IdGeneration ID_GENERATION = new IdGeneration() { 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 6979c365e9..a06b4e3b25 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 @@ -49,14 +49,19 @@ public class PostgresDialect extends AbstractDialect { /** * Singleton instance. + * + * @deprecated use either the {@code org.springframework.data.r2dbc.dialect.PostgresDialect} or + * {@code org.springframework.data.jdbc.core.dialect.JdbcPostgresDialect}. */ + @Deprecated(forRemoval = true) public static final PostgresDialect INSTANCE = new PostgresDialect(); private static final Set> POSTGRES_SIMPLE_TYPES = Set.of(UUID.class, URL.class, URI.class, InetAddress.class, Map.class); - private IdentifierProcessing identifierProcessing = IdentifierProcessing.create(Quoting.ANSI, + private static final IdentifierProcessing identifierProcessing = IdentifierProcessing.create(Quoting.ANSI, LetterCasing.LOWER_CASE); + private IdGeneration idGeneration = new IdGeneration() { @Override diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java index 36f0381f84..ad3b706158 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java @@ -34,7 +34,11 @@ public class SqlServerDialect extends AbstractDialect { /** * Singleton instance. + * + * @deprecated use either the {@code org.springframework.data.r2dbc.dialect.SqlServerDialect} or + * {@code org.springframework.data.jdbc.core.dialect.JdbcSqlServerDialect}. */ + @Deprecated(forRemoval = true) public static final SqlServerDialect INSTANCE = new SqlServerDialect(); private static final IdentifierProcessing IDENTIFIER_PROCESSING = IdentifierProcessing diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/HsqlDbDialectUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/HsqlDbDialectUnitTests.java index 17d3ef771d..b03eb034a0 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/HsqlDbDialectUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/HsqlDbDialectUnitTests.java @@ -15,14 +15,14 @@ */ package org.springframework.data.relational.core.dialect; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + import org.junit.jupiter.api.Test; import org.springframework.data.relational.core.sql.From; import org.springframework.data.relational.core.sql.LockMode; import org.springframework.data.relational.core.sql.LockOptions; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - /** * Unit tests for the {@link HsqlDbDialect}. * @@ -34,7 +34,7 @@ public class HsqlDbDialectUnitTests { @Test // DATAJDBC-386 public void shouldNotSupportArrays() { - ArrayColumns arrayColumns = HsqlDbDialect.INSTANCE.getArraySupport(); + ArrayColumns arrayColumns = new HsqlDbDialect().getArraySupport(); assertThat(arrayColumns.isSupported()).isFalse(); } @@ -42,7 +42,7 @@ public void shouldNotSupportArrays() { @Test // DATAJDBC-386 public void shouldRenderLimit() { - LimitClause limit = HsqlDbDialect.INSTANCE.limit(); + LimitClause limit = new HsqlDbDialect().limit(); assertThat(limit.getClausePosition()).isEqualTo(LimitClause.Position.AFTER_ORDER_BY); assertThat(limit.getLimit(10)).isEqualTo("LIMIT 10"); @@ -51,7 +51,7 @@ public void shouldRenderLimit() { @Test // DATAJDBC-386 public void shouldRenderOffset() { - LimitClause limit = HsqlDbDialect.INSTANCE.limit(); + LimitClause limit = new HsqlDbDialect().limit(); assertThat(limit.getOffset(10)).isEqualTo("OFFSET 10"); } @@ -59,7 +59,7 @@ public void shouldRenderOffset() { @Test // DATAJDBC-386 public void shouldRenderLimitOffset() { - LimitClause limit = HsqlDbDialect.INSTANCE.limit(); + LimitClause limit = new HsqlDbDialect().limit(); assertThat(limit.getLimitOffset(20, 10)).isEqualTo("OFFSET 10 LIMIT 20"); } @@ -67,7 +67,7 @@ public void shouldRenderLimitOffset() { @Test // DATAJDBC-386 public void shouldQuoteIdentifiersUsingBackticks() { - String abcQuoted = HsqlDbDialect.INSTANCE.getIdentifierProcessing().quote("abc"); + String abcQuoted = new HsqlDbDialect().getIdentifierProcessing().quote("abc"); assertThat(abcQuoted).isEqualTo("\"abc\""); } @@ -75,7 +75,7 @@ public void shouldQuoteIdentifiersUsingBackticks() { @Test // DATAJDBC-498 public void shouldRenderLock() { - LockClause limit = HsqlDbDialect.INSTANCE.lock(); + LockClause limit = new HsqlDbDialect().lock(); From from = mock(From.class); LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_WRITE, from); diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectRenderingUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectRenderingUnitTests.java index 96f64c2036..2ad7733412 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectRenderingUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectRenderingUnitTests.java @@ -36,7 +36,7 @@ */ public class MySqlDialectRenderingUnitTests { - private final RenderContextFactory factory = new RenderContextFactory(MySqlDialect.INSTANCE); + private final RenderContextFactory factory = new RenderContextFactory(new MySqlDialect()); @BeforeEach public void before() { diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java index 51736ef0f7..d9112a4dde 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java @@ -35,7 +35,7 @@ public class MySqlDialectUnitTests { @Test // DATAJDBC-278 public void shouldNotSupportArrays() { - ArrayColumns arrayColumns = MySqlDialect.INSTANCE.getArraySupport(); + ArrayColumns arrayColumns = new MySqlDialect().getArraySupport(); assertThat(arrayColumns.isSupported()).isFalse(); } @@ -43,7 +43,7 @@ public void shouldNotSupportArrays() { @Test // DATAJDBC-278 public void shouldRenderLimit() { - LimitClause limit = MySqlDialect.INSTANCE.limit(); + LimitClause limit = new MySqlDialect().limit(); assertThat(limit.getClausePosition()).isEqualTo(LimitClause.Position.AFTER_ORDER_BY); assertThat(limit.getLimit(10)).isEqualTo("LIMIT 10"); @@ -52,7 +52,7 @@ public void shouldRenderLimit() { @Test // DATAJDBC-278 public void shouldRenderOffset() { - LimitClause limit = MySqlDialect.INSTANCE.limit(); + LimitClause limit = new MySqlDialect().limit(); assertThat(limit.getOffset(10)).isEqualTo("LIMIT 10, 18446744073709551615"); } @@ -60,7 +60,7 @@ public void shouldRenderOffset() { @Test // DATAJDBC-278 public void shouldRenderLimitOffset() { - LimitClause limit = MySqlDialect.INSTANCE.limit(); + LimitClause limit = new MySqlDialect().limit(); assertThat(limit.getLimitOffset(20, 10)).isEqualTo("LIMIT 10, 20"); } @@ -68,7 +68,7 @@ public void shouldRenderLimitOffset() { @Test // DATAJDBC-386 public void shouldQuoteIdentifiersUsingBackticks() { - String abcQuoted = MySqlDialect.INSTANCE.getIdentifierProcessing().quote("abc"); + String abcQuoted = new MySqlDialect().getIdentifierProcessing().quote("abc"); assertThat(abcQuoted).isEqualTo("`abc`"); } @@ -76,7 +76,7 @@ public void shouldQuoteIdentifiersUsingBackticks() { @Test // DATAJDBC-498 public void shouldRenderLock() { - LockClause lock = MySqlDialect.INSTANCE.lock(); + LockClause lock = new MySqlDialect().lock(); From from = mock(From.class); assertThat(lock.getLock(new LockOptions(LockMode.PESSIMISTIC_WRITE, from))).isEqualTo("FOR UPDATE"); diff --git a/src/main/antora/modules/ROOT/pages/jdbc/getting-started.adoc b/src/main/antora/modules/ROOT/pages/jdbc/getting-started.adoc index 84abb44060..ed59a627c6 100644 --- a/src/main/antora/modules/ROOT/pages/jdbc/getting-started.adoc +++ b/src/main/antora/modules/ROOT/pages/jdbc/getting-started.adoc @@ -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=` +`org.springframework.data.jdbc.core.dialect.DialectResolver$JdbcDialectProvider`=` ====