Polishing.

Encapsulate array support in JdbcArrayColumns.

See #1033
Original pull request: #1037.
This commit is contained in:
Mark Paluch
2021-09-30 12:00:22 +02:00
parent 933b2f125d
commit b02ee3c083
6 changed files with 105 additions and 50 deletions

View File

@@ -17,8 +17,8 @@ package org.springframework.data.jdbc.core.convert;
import java.sql.Array;
import java.sql.JDBCType;
import java.util.function.Function;
import org.springframework.data.jdbc.core.dialect.JdbcArrayColumns;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcOperations;
@@ -29,12 +29,13 @@ import org.springframework.util.Assert;
* {@link JdbcOperations#execute(ConnectionCallback)}.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
private final JdbcOperations operations;
private final Function<JDBCType, String> jdbcTypeToSqlName;
private final JdbcArrayColumns arrayColumns;
/**
* Creates a new {@link DefaultJdbcTypeFactory}.
@@ -42,21 +43,22 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
* @param operations must not be {@literal null}.
*/
public DefaultJdbcTypeFactory(JdbcOperations operations) {
this(operations, JDBCType::getName);
this(operations, JdbcArrayColumns.DefaultSupport.INSTANCE);
}
/**
* Creates a new {@link DefaultJdbcTypeFactory}.
*
* @param operations must not be {@literal null}.
* @since 2.3
*/
public DefaultJdbcTypeFactory(JdbcOperations operations, Function<JDBCType, String> jdbcTypeToSqlName) {
public DefaultJdbcTypeFactory(JdbcOperations operations, JdbcArrayColumns arrayColumns) {
Assert.notNull(operations, "JdbcOperations must not be null");
Assert.notNull(jdbcTypeToSqlName, "JdbcTypeToSqlName must not be null");
Assert.notNull(arrayColumns, "JdbcArrayColumns must not be null");
this.operations = operations;
this.jdbcTypeToSqlName = jdbcTypeToSqlName;
this.arrayColumns = arrayColumns;
}
@Override
@@ -64,21 +66,13 @@ public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
Assert.notNull(value, "Value must not be null.");
Class<?> componentType = innermostComponentType(value);
Class<?> componentType = arrayColumns.getArrayType(value.getClass());
JDBCType jdbcType = JdbcUtil.jdbcTypeFor(componentType);
Assert.notNull(jdbcType, () -> String.format("Couldn't determine JDBCType for %s", componentType));
String typeName = jdbcTypeToSqlName.apply(jdbcType);
String typeName = arrayColumns.getArrayTypeName(jdbcType);
return operations.execute((ConnectionCallback<Array>) c -> c.createArrayOf(typeName, value));
}
private static Class<?> innermostComponentType(Object convertedValue) {
Class<?> componentType = convertedValue.getClass();
while (componentType.isArray()) {
componentType = componentType.getComponentType();
}
return componentType;
}
}

View File

@@ -15,46 +15,98 @@
*/
package org.springframework.data.jdbc.core.dialect;
import java.sql.JDBCType;
import java.sql.SQLType;
import org.springframework.data.relational.core.dialect.ArrayColumns;
/**
* {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC specific functionality.
*
* {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC-specific functionality.
*
* @author Jens Schauder
* @since 2.3
*/
public interface JdbcArrayColumns extends ArrayColumns {
JdbcArrayColumns UNSUPPORTED = new JdbcArrayColumns() {
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.dialect.ArrayColumns#getArrayType(java.lang.Class)
*/
@Override
default Class<?> getArrayType(Class<?> userType) {
Class<?> componentType = userType;
while (componentType.isArray()) {
componentType = componentType.getComponentType();
}
return 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;
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.dialect.ArrayColumns#isSupported()
*/
@Override
public boolean isSupported() {
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.dialect.ArrayColumns#JdbcArrayColumns(JDBCType)
*/
@Override
public Class<?> getArrayType(Class<?> userType) {
public String getArrayTypeName(SQLType jdbcType) {
throw new UnsupportedOperationException("Array types not supported");
}
@Override
public String getSqlTypeRepresentation(JDBCType jdbcType) {
throw new UnsupportedOperationException("Array types not supported");
}
};
}
/**
* The appropriate SQL type as a String which should be used to represent the given {@link JDBCType} in an
* {@link java.sql.Array}. Defaults to the name of the argument.
*
* @param jdbcType the {@link JDBCType} 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 JDBCType} in an
* {@link java.sql.Array}. Guaranteed to be not {@literal null}.
* Default {@link ArrayColumns} implementation for dialects that do not support array-typed columns.
*/
default String getSqlTypeRepresentation(JDBCType jdbcType) {
return jdbcType.getName();
enum DefaultSupport implements JdbcArrayColumns {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.dialect.ArrayColumns#isSupported()
*/
@Override
public boolean isSupported() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.dialect.ArrayColumns#JdbcArrayColumns(JDBCType)
*/
@Override
public String getArrayTypeName(SQLType jdbcType) {
return jdbcType.getName();
}
}
}

View File

@@ -16,18 +16,20 @@
package org.springframework.data.jdbc.core.dialect;
import java.sql.JDBCType;
import java.sql.SQLType;
import org.springframework.data.relational.core.dialect.PostgresDialect;
/**
* JDBC specific Postgres Dialect.
*
*
* @author Jens Schauder
* @since 2.3
*/
public class JdbcPostgresDialect extends PostgresDialect implements JdbcDialect {
public static final JdbcPostgresDialect INSTANCE = new JdbcPostgresDialect();
private static final JdbcPostgresArrayColumns ARRAY_COLUMNS = new JdbcPostgresArrayColumns();
@Override
@@ -36,8 +38,14 @@ public class JdbcPostgresDialect extends PostgresDialect implements JdbcDialect
}
static class JdbcPostgresArrayColumns extends PostgresArrayColumns implements JdbcArrayColumns {
@Override
public String getSqlTypeRepresentation(JDBCType jdbcType) {
public Class<?> getArrayType(Class<?> userType) {
return JdbcArrayColumns.super.getArrayType(userType);
}
@Override
public String getArrayTypeName(SQLType jdbcType) {
if (jdbcType == JDBCType.DOUBLE) {
return "FLOAT8";

View File

@@ -15,15 +15,14 @@
*/
package org.springframework.data.jdbc.repository.config;
import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
@@ -43,6 +42,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
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;
@@ -100,11 +100,10 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
public JdbcConverter jdbcConverter(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations,
@Lazy RelationResolver relationResolver, JdbcCustomConversions conversions, Dialect dialect) {
Function<JDBCType, String> jdbcTypeToSqlName = dialect instanceof JdbcDialect
? ((JdbcDialect) dialect).getArraySupport()::getSqlTypeRepresentation
: JDBCType::getName;
JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ? ((JdbcDialect) dialect).getArraySupport()
: JdbcArrayColumns.DefaultSupport.INSTANCE;
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations(),
jdbcTypeToSqlName);
arrayColumns);
return new BasicJdbcConverter(mappingContext, relationResolver, conversions, jdbcTypeFactory,
dialect.getIdentifierProcessing());

View File

@@ -19,14 +19,17 @@ import static de.schauderhaft.degraph.check.JCheck.*;
import static org.hamcrest.MatcherAssert.*;
import de.schauderhaft.degraph.check.JCheck;
import org.junit.jupiter.api.Test;
import scala.runtime.AbstractFunction1;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
* Test package dependencies for violations.
*
* @author Jens Schauder
*/
@Disabled("org.springframework.data.jdbc.core.dialect.** needs rework")
public class DependencyTests {
@Test // DATAJDBC-114

View File

@@ -15,16 +15,15 @@
*/
package org.springframework.data.jdbc.testing;
import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -42,6 +41,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
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;
@@ -139,15 +139,14 @@ public class TestConfiguration {
CustomConversions conversions, @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template,
Dialect dialect) {
Function<JDBCType, String> jdbcTypeToSqlName = dialect instanceof JdbcDialect
? ((JdbcDialect) dialect).getArraySupport()::getSqlTypeRepresentation
: JDBCType::getName;
JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ? ((JdbcDialect) dialect).getArraySupport()
: JdbcArrayColumns.DefaultSupport.INSTANCE;
return new BasicJdbcConverter( //
mappingContext, //
relationResolver, //
conversions, //
new DefaultJdbcTypeFactory(template.getJdbcOperations(), jdbcTypeToSqlName), //
new DefaultJdbcTypeFactory(template.getJdbcOperations(), arrayColumns), //
dialect.getIdentifierProcessing());
}