Browse Source

Introduce `JdbcConfiguration` and extract factory methods used from `AbstractJdbcConfiguration`.

Closes: #2165
Original pull request: #2166
pull/2173/head
Mark Paluch 2 months ago
parent
commit
a28a2cd517
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 83
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java
  2. 168
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java
  3. 5
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java
  4. 7
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfigurationIntegrationTests.java
  5. 6
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfigurationIntegrationTests.java
  6. 12
      src/main/antora/modules/ROOT/pages/jdbc/getting-started.adoc
  7. 4
      src/main/antora/modules/ROOT/pages/jdbc/mapping.adoc
  8. 8
      src/main/antora/modules/ROOT/pages/r2dbc/getting-started.adoc
  9. 2
      src/main/antora/modules/ROOT/pages/r2dbc/mapping.adoc

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

@ -15,7 +15,6 @@
*/ */
package org.springframework.data.jdbc.repository.config; package org.springframework.data.jdbc.repository.config;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -23,8 +22,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
@ -32,34 +29,23 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.JdbcAggregateOperations; import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.DataAccessStrategyFactory;
import org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory;
import org.springframework.data.jdbc.core.convert.IdGeneratingEntityCallback; import org.springframework.data.jdbc.core.convert.IdGeneratingEntityCallback;
import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.convert.MappingJdbcConverter;
import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration;
import org.springframework.data.jdbc.core.convert.RelationResolver; import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.dialect.DialectResolver; import org.springframework.data.jdbc.core.dialect.DialectResolver;
import org.springframework.data.jdbc.core.dialect.JdbcArrayColumns;
import org.springframework.data.jdbc.core.dialect.JdbcDialect; import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.relational.RelationalManagedTypes; import org.springframework.data.relational.RelationalManagedTypes;
import org.springframework.data.relational.core.conversion.RelationalConverter; import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.DefaultNamingStrategy;
import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.util.TypeScanner;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.StringUtils;
/** /**
* Beans that must be registered for Spring Data JDBC to work. * Beans that must be registered for Spring Data JDBC to work.
@ -77,8 +63,6 @@ import org.springframework.util.StringUtils;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public class AbstractJdbcConfiguration implements ApplicationContextAware { public class AbstractJdbcConfiguration implements ApplicationContextAware {
private static final Log LOG = LogFactory.getLog(AbstractJdbcConfiguration.class);
@SuppressWarnings("NullAway.Init") private ApplicationContext applicationContext; @SuppressWarnings("NullAway.Init") private ApplicationContext applicationContext;
private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY; private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
@ -96,7 +80,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
protected Collection<String> getMappingBasePackages() { protected Collection<String> getMappingBasePackages() {
Package mappingBasePackage = getClass().getPackage(); Package mappingBasePackage = getClass().getPackage();
return Collections.singleton(mappingBasePackage == null ? null : mappingBasePackage.getName()); return mappingBasePackage == null ? List.of() : List.of(mappingBasePackage.getName());
} }
/** /**
@ -124,13 +108,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
@Bean @Bean
public JdbcMappingContext jdbcMappingContext(Optional<NamingStrategy> namingStrategy, public JdbcMappingContext jdbcMappingContext(Optional<NamingStrategy> namingStrategy,
JdbcCustomConversions customConversions, RelationalManagedTypes jdbcManagedTypes) { JdbcCustomConversions customConversions, RelationalManagedTypes jdbcManagedTypes) {
return JdbcConfiguration.createMappingContext(jdbcManagedTypes, customConversions, namingStrategy.orElse(null));
JdbcMappingContext mappingContext = JdbcMappingContext
.forQuotedIdentifiers(namingStrategy.orElse(DefaultNamingStrategy.INSTANCE));
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setManagedTypes(jdbcManagedTypes);
return mappingContext;
} }
/** /**
@ -143,7 +121,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
*/ */
@Bean @Bean
public IdGeneratingEntityCallback idGeneratingBeforeSaveCallback(JdbcMappingContext mappingContext, public IdGeneratingEntityCallback idGeneratingBeforeSaveCallback(JdbcMappingContext mappingContext,
NamedParameterJdbcOperations operations, Dialect dialect) { NamedParameterJdbcOperations operations, JdbcDialect dialect) {
return new IdGeneratingEntityCallback(mappingContext, dialect, operations); return new IdGeneratingEntityCallback(mappingContext, dialect, operations);
} }
@ -157,25 +135,14 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
*/ */
@Bean @Bean
public JdbcConverter jdbcConverter(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations, public JdbcConverter jdbcConverter(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations,
@Lazy RelationResolver relationResolver, JdbcCustomConversions conversions, Dialect dialect) { @Lazy RelationResolver relationResolver, JdbcCustomConversions conversions, JdbcDialect dialect) {
return JdbcConfiguration.createConverter(mappingContext, operations, relationResolver, conversions, dialect);
JdbcArrayColumns arrayColumns = JdbcDialect.getArraySupport(dialect);
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations(), arrayColumns);
MappingJdbcConverter mappingJdbcConverter = new MappingJdbcConverter(mappingContext, relationResolver, conversions,
jdbcTypeFactory);
if (operations.getJdbcOperations() instanceof JdbcTemplate jdbcTemplate) {
mappingJdbcConverter.setExceptionTranslator(jdbcTemplate.getExceptionTranslator());
}
return mappingJdbcConverter;
} }
/** /**
* Register custom {@link Converter}s in a {@link JdbcCustomConversions} object if required. These * Register custom {@link Converter}s in a {@link JdbcCustomConversions} object if required. These
* {@link JdbcCustomConversions} will be registered with the * {@link JdbcCustomConversions} will be registered with the
* {@link #jdbcConverter(JdbcMappingContext, NamedParameterJdbcOperations, RelationResolver, JdbcCustomConversions, Dialect)}. * {@link #jdbcConverter(JdbcMappingContext, NamedParameterJdbcOperations, RelationResolver, JdbcCustomConversions, JdbcDialect)}.
* Returns an empty {@link JdbcCustomConversions} instance by default. * Returns an empty {@link JdbcCustomConversions} instance by default.
* *
* @return will never be {@literal null}. * @return will never be {@literal null}.
@ -183,31 +150,14 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
@Bean @Bean
public JdbcCustomConversions jdbcCustomConversions() { public JdbcCustomConversions jdbcCustomConversions() {
Dialect dialect = applicationContext.getBeanProvider(Dialect.class).getIfAvailable(); JdbcDialect dialect = applicationContext.getBean(JdbcDialect.class);
return JdbcConfiguration.createCustomConversions(dialect, userConverters());
if (dialect == null) {
LOG.warn("No JdbcDialect bean found; CustomConversions will be configured without dialect-specific types.");
return new JdbcCustomConversions();
}
SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)),
userConverters());
} }
protected List<?> userConverters() { protected List<?> userConverters() {
return Collections.emptyList(); return Collections.emptyList();
} }
private List<Object> storeConverters(Dialect dialect) {
List<Object> converters = new ArrayList<>();
converters.addAll(dialect.getConverters());
converters.addAll(JdbcCustomConversions.storeConverters());
return converters;
}
/** /**
* Register a {@link JdbcAggregateTemplate} as a bean for easy use in applications that need a lower level of * Register a {@link JdbcAggregateTemplate} as a bean for easy use in applications that need a lower level of
* abstraction than the normal repository abstraction. * abstraction than the normal repository abstraction.
@ -232,8 +182,8 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
*/ */
@Bean @Bean
public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter, public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter,
JdbcMappingContext context, Dialect dialect) { JdbcMappingContext context, JdbcDialect dialect) {
return new DataAccessStrategyFactory(jdbcConverter, operations, dialect, this.queryMappingConfiguration).create(); return JdbcConfiguration.createDataAccessStrategy(operations, jdbcConverter, queryMappingConfiguration, dialect);
} }
/** /**
@ -245,7 +195,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
* @throws DialectResolver.NoDialectException if the {@link Dialect} cannot be determined. * @throws DialectResolver.NoDialectException if the {@link Dialect} cannot be determined.
*/ */
@Bean @Bean
public Dialect jdbcDialect(NamedParameterJdbcOperations operations) { public JdbcDialect jdbcDialect(NamedParameterJdbcOperations operations) {
return DialectResolver.getDialect(operations.getJdbcOperations()); return DialectResolver.getDialect(operations.getJdbcOperations());
} }
@ -286,16 +236,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
* @return a set of classes identified as entities. * @return a set of classes identified as entities.
* @since 3.0 * @since 3.0
*/ */
@SuppressWarnings("unchecked")
protected Set<Class<?>> scanForEntities(String basePackage) { protected Set<Class<?>> scanForEntities(String basePackage) {
return JdbcConfiguration.scanForEntities(basePackage);
if (!StringUtils.hasText(basePackage)) {
return Collections.emptySet();
}
return TypeScanner.typeScanner(AbstractJdbcConfiguration.class.getClassLoader()) //
.forTypesAnnotatedWith(Table.class) //
.scanPackages(basePackage) //
.collectAsSet();
} }
} }

168
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java

@ -0,0 +1,168 @@
/*
* 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.repository.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.DataAccessStrategyFactory;
import org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory;
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.QueryMappingConfiguration;
import org.springframework.data.jdbc.core.convert.RelationResolver;
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;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.relational.RelationalManagedTypes;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.DefaultNamingStrategy;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.util.TypeScanner;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.StringUtils;
/**
* Utility class to providing factory methods for JDBC infrastructure components.
* <p>
* Mainly for use within the framework or for configuration arrangements that require customization of configuration.
*
* @author Mark Paluch
* @since 4.0
*/
public final class JdbcConfiguration {
private JdbcConfiguration() {}
/**
* Register custom {@link Converter}s in a {@link JdbcCustomConversions} object if required.
*
* @param dialect the JDBC dialect in use.
* @param userConverters list of user converters, must not be {@literal null}.
* @return will never be {@literal null}.
*/
public static JdbcCustomConversions createCustomConversions(JdbcDialect dialect, List<?> userConverters) {
SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)),
userConverters);
}
private static List<Object> storeConverters(Dialect dialect) {
List<Object> converters = new ArrayList<>();
converters.addAll(dialect.getConverters());
converters.addAll(JdbcCustomConversions.storeConverters());
return converters;
}
/**
* Register a {@link JdbcMappingContext} and apply an optional {@link NamingStrategy}.
*
* @param jdbcManagedTypes JDBC managed types.
* @param customConversions the custom conversions.
* @param namingStrategy optional {@link NamingStrategy}. Use {@link DefaultNamingStrategy#INSTANCE} as fallback.
* @return must not be {@literal null}.
*/
public static JdbcMappingContext createMappingContext(RelationalManagedTypes jdbcManagedTypes,
JdbcCustomConversions customConversions, @Nullable NamingStrategy namingStrategy) {
JdbcMappingContext mappingContext = JdbcMappingContext
.forQuotedIdentifiers(namingStrategy != null ? namingStrategy : DefaultNamingStrategy.INSTANCE);
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setManagedTypes(jdbcManagedTypes);
return mappingContext;
}
/**
* Creates a {@link JdbcConverter}.
*
* @param mappingContext must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param relationResolver must not be {@literal null}.
* @param conversions must not be {@literal null}.
* @param dialect the JDBC dialect in use.
* @return must not be {@literal null}.
*/
public static JdbcConverter createConverter(JdbcMappingContext mappingContext,
NamedParameterJdbcOperations operations, RelationResolver relationResolver, JdbcCustomConversions conversions,
JdbcDialect dialect) {
JdbcArrayColumns arrayColumns = JdbcDialect.getArraySupport(dialect);
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations(), arrayColumns);
MappingJdbcConverter mappingJdbcConverter = new MappingJdbcConverter(mappingContext, relationResolver, conversions,
jdbcTypeFactory);
if (operations.getJdbcOperations() instanceof JdbcTemplate jdbcTemplate) {
mappingJdbcConverter.setExceptionTranslator(jdbcTemplate.getExceptionTranslator());
}
return mappingJdbcConverter;
}
/**
* Create a {@link DataAccessStrategy} for reuse in the {@link JdbcAggregateOperations} and the {@link JdbcConverter}.
* Override this method to register a bean of type {@link DataAccessStrategy} if your use case requires a more
* specialized {@link DataAccessStrategy}.
*
* @param operations must not be {@literal null}.
* @param jdbcConverter must not be {@literal null}.
* @param mappingConfiguration mapping configuration, can be {@literal null}.
* @param dialect the JDBC dialect in use.
* @return will never be {@literal null}.
*/
public static DataAccessStrategy createDataAccessStrategy(NamedParameterJdbcOperations operations,
JdbcConverter jdbcConverter, @Nullable QueryMappingConfiguration mappingConfiguration, JdbcDialect dialect) {
return new DataAccessStrategyFactory(jdbcConverter, operations, dialect,
mappingConfiguration == null ? QueryMappingConfiguration.EMPTY : mappingConfiguration).create();
}
/**
* Scans the given base package for entities, i.e. JDBC-specific types annotated with {@link Table}.
*
* @param basePackage must not be {@literal null}.
* @return a set of classes identified as entities.
*/
@SuppressWarnings("unchecked")
public static Set<Class<?>> scanForEntities(String basePackage) {
if (!StringUtils.hasText(basePackage)) {
return Collections.emptySet();
}
return TypeScanner.typeScanner(JdbcConfiguration.class.getClassLoader()) //
.forTypesAnnotatedWith(Table.class) //
.scanPackages(basePackage) //
.collectAsSet();
}
}

5
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java

@ -18,15 +18,16 @@ package org.springframework.data.jdbc.repository.config;
import java.util.Optional; import java.util.Optional;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/** /**
@ -46,7 +47,7 @@ public class MyBatisJdbcConfiguration extends AbstractJdbcConfiguration {
@Bean @Bean
@Override @Override
public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter, public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter,
JdbcMappingContext context, Dialect dialect) { JdbcMappingContext context, JdbcDialect dialect) {
return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session, dialect, return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session, dialect,
queryMappingConfiguration.orElse(QueryMappingConfiguration.EMPTY)); queryMappingConfiguration.orElse(QueryMappingConfiguration.EMPTY));

7
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfigurationIntegrationTests.java

@ -25,6 +25,7 @@ import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -36,9 +37,9 @@ import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.RelationalManagedTypes; import org.springframework.data.relational.RelationalManagedTypes;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.LimitClause; import org.springframework.data.relational.core.dialect.LimitClause;
import org.springframework.data.relational.core.dialect.LockClause; import org.springframework.data.relational.core.dialect.LockClause;
import org.springframework.data.relational.core.sql.render.SelectRenderContext; import org.springframework.data.relational.core.sql.render.SelectRenderContext;
@ -142,7 +143,7 @@ class AbstractJdbcConfigurationIntegrationTests {
@Override @Override
@Bean @Bean
public Dialect jdbcDialect(NamedParameterJdbcOperations operations) { public JdbcDialect jdbcDialect(NamedParameterJdbcOperations operations) {
return new DummyDialect(); return new DummyDialect();
} }
@ -165,7 +166,7 @@ class AbstractJdbcConfigurationIntegrationTests {
private static class Blubb {} private static class Blubb {}
private static class DummyDialect implements Dialect { private static class DummyDialect implements JdbcDialect {
@Override @Override
public LimitClause limit() { public LimitClause limit() {
return null; return null;

6
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfigurationIntegrationTests.java

@ -22,14 +22,14 @@ import java.util.List;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.core.convert.CascadingDataAccessStrategy; import org.springframework.data.jdbc.core.convert.CascadingDataAccessStrategy;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect; import org.springframework.data.jdbc.core.dialect.JdbcHsqlDbDialect;
import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
@ -70,7 +70,7 @@ public class MyBatisJdbcConfigurationIntegrationTests extends AbstractJdbcConfig
@Override @Override
@Bean @Bean
public Dialect jdbcDialect(NamedParameterJdbcOperations operations) { public JdbcDialect jdbcDialect(NamedParameterJdbcOperations operations) {
return JdbcHsqlDbDialect.INSTANCE; return JdbcHsqlDbDialect.INSTANCE;
} }
} }

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

@ -148,13 +148,13 @@ There are a couple of things one might want to customize in this setup.
[[jdbc.dialects]] [[jdbc.dialects]]
== Dialects == Dialects
Spring Data JDBC uses implementations of the interface `Dialect` to encapsulate behavior that is specific to a database or its JDBC driver. Spring Data JDBC uses implementations of the interface `JdbcDialect` to encapsulate behavior that is specific to a database or its JDBC driver.
By default, the javadoc:org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration[] attempts to determine the dialect from the database configuration by obtaining a connection and registering the correct `Dialect`. By default, the javadoc:org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration[] attempts to determine the dialect from the database configuration by obtaining a connection and registering the correct `JdbcDialect`.
You override `AbstractJdbcConfiguration.jdbcDialect(NamedParameterJdbcOperations)` to customize dialect selection. You override `AbstractJdbcConfiguration.jdbcDialect(NamedParameterJdbcOperations)` to customize dialect selection.
If you use a database for which no dialect is available, then your application won’t start up. If you use a database for which no dialect is available, then your application won’t start up.
In that case, you’ll have to ask your vendor to provide a `Dialect` implementation. In that case, you’ll have to ask your vendor to provide a `JdbcDialect` implementation.
Alternatively, you can implement your own `Dialect`. Alternatively, you can implement your own `JdbcDialect`.
[TIP] [TIP]
==== ====
@ -163,8 +163,8 @@ You can let Spring auto-discover your javadoc:org.springframework.data.jdbc.core
`DialectResolver` discovers dialect provider implementations from the class path using Spring's `SpringFactoriesLoader`. `DialectResolver` discovers dialect provider implementations from the class path using Spring's `SpringFactoriesLoader`.
To do so: To do so:
. Implement your own `Dialect`. . Implement your own `JdbcDialect`.
. Implement a `JdbcDialectProvider` returning the `Dialect`. . Implement a `JdbcDialectProvider` returning the `JdbcDialect`.
. Register the provider by creating a `spring.factories` resource under `META-INF` and perform the registration by adding a line + . Register the provider by creating a `spring.factories` resource under `META-INF` and perform the registration by adding a line +
`org.springframework.data.jdbc.core.dialect.DialectResolver$JdbcDialectProvider`=<fully qualified name of your JdbcDialectProvider>`. `org.springframework.data.jdbc.core.dialect.DialectResolver$JdbcDialectProvider`=<fully qualified name of your JdbcDialectProvider>`.
==== ====

4
src/main/antora/modules/ROOT/pages/jdbc/mapping.adoc

@ -208,8 +208,8 @@ class MyJdbcConfiguration extends AbstractJdbcConfiguration {
---- ----
NOTE: In previous versions of Spring Data JDBC it was recommended to directly overwrite `AbstractJdbcConfiguration.jdbcCustomConversions()`. NOTE: In previous versions of Spring Data JDBC it was recommended to directly overwrite `AbstractJdbcConfiguration.jdbcCustomConversions()`.
This is no longer necessary or even recommended, since that method assembles conversions intended for all databases, conversions registered by the `Dialect` used and conversions registered by the user. This is no longer necessary or even recommended, since that method assembles conversions intended for all databases, conversions registered by the `JdbcDialect` used and conversions registered by the user.
If you are migrating from an older version of Spring Data JDBC and have `AbstractJdbcConfiguration.jdbcCustomConversions()` overwritten conversions from your `Dialect` will not get registered. If you are migrating from an older version of Spring Data JDBC and have `AbstractJdbcConfiguration.jdbcCustomConversions()` overwritten conversions from your `JdbcDialect` will not get registered.
[TIP] [TIP]
==== ====

8
src/main/antora/modules/ROOT/pages/r2dbc/getting-started.adoc

@ -172,15 +172,15 @@ This approach lets you use the standard `io.r2dbc.spi.ConnectionFactory` instanc
[[r2dbc.dialects]] [[r2dbc.dialects]]
== Dialects == Dialects
Spring Data R2DBC uses a `Dialect` to encapsulate behavior that is specific to a database or its driver. Spring Data R2DBC uses a `R2dbcDialect` to encapsulate behavior that is specific to a database or its driver.
Spring Data R2DBC reacts to database specifics by inspecting the `ConnectionFactory` and selects the appropriate database dialect accordingly. Spring Data R2DBC reacts to database specifics by inspecting the `ConnectionFactory` and selects the appropriate database dialect accordingly.
If you use a database for which no dialect is available, then your application won’t start up. If you use a database for which no dialect is available, then your application won’t start up.
In that case, you’ll have to ask your vendor to provide a `Dialect` implementation. In that case, you’ll have to ask your vendor to provide a `R2dbcDialect` implementation.
Alternatively, you can implement your own `Dialect`. Alternatively, you can implement your own `R2dbcDialect`.
[TIP] [TIP]
==== ====
Dialects are resolved by javadoc:org.springframework.data.r2dbc./dialect.DialectResolver[] from a `ConnectionFactory`, typically by inspecting `ConnectionFactoryMetadata`. Dialects are resolved by javadoc:org.springframework.data.r2dbc.dialect.DialectResolver[] from a `ConnectionFactory`, typically by inspecting `ConnectionFactoryMetadata`.
+ You can let Spring auto-discover your `R2dbcDialect` by registering a class that implements `org.springframework.data.r2dbc.dialect.DialectResolver$R2dbcDialectProvider` through `META-INF/spring.factories`. + You can let Spring auto-discover your `R2dbcDialect` by registering a class that implements `org.springframework.data.r2dbc.dialect.DialectResolver$R2dbcDialectProvider` through `META-INF/spring.factories`.
`DialectResolver` discovers dialect provider implementations from the class path using Spring's `SpringFactoriesLoader`. `DialectResolver` discovers dialect provider implementations from the class path using Spring's `SpringFactoriesLoader`.
To do so: To do so:

2
src/main/antora/modules/ROOT/pages/r2dbc/mapping.adoc

@ -53,7 +53,7 @@ You can do so by overriding `r2dbcMappingContext(Optional<NamingStrategy>)` of `
Spring Data converts the letter casing of such a name to that form which is also used by the configured database when no quoting is used. Spring Data converts the letter casing of such a name to that form which is also used by the configured database when no quoting is used.
Therefore, you can use unquoted names when creating tables, as long as you do not use keywords or special characters in your names. Therefore, you can use unquoted names when creating tables, as long as you do not use keywords or special characters in your names.
For databases that adhere to the SQL standard, this means that names are converted to upper case. For databases that adhere to the SQL standard, this means that names are converted to upper case.
The quoting character and the way names get capitalized is controlled by the used `Dialect`. The quoting character and the way names get capitalized is controlled by the used `R2dbcDialect`.
See xref:r2dbc/getting-started.adoc#r2dbc.dialects[R2DBC Drivers] for how to configure custom dialects. See xref:r2dbc/getting-started.adoc#r2dbc.dialects[R2DBC Drivers] for how to configure custom dialects.
.@Configuration class to configure R2DBC mapping support .@Configuration class to configure R2DBC mapping support

Loading…
Cancel
Save