Skip to content

SQL type used in array construction depends on Dialect. #1037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>2.3.0-SNAPSHOT</version>
<version>2.3.0-1033-double-array-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data Relational Parent</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-jdbc-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>2.3.0-SNAPSHOT</version>
<version>2.3.0-1033-double-array-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-jdbc</artifactId>
<version>2.3.0-SNAPSHOT</version>
<version>2.3.0-1033-double-array-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>2.3.0-SNAPSHOT</version>
<version>2.3.0-1033-double-array-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.sql.Array;
import java.sql.JDBCType;

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;
Expand All @@ -28,44 +29,50 @@
* {@link JdbcOperations#execute(ConnectionCallback)}.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
public class DefaultJdbcTypeFactory implements JdbcTypeFactory {

private final JdbcOperations operations;
private final JdbcArrayColumns arrayColumns;

/**
* Creates a new {@link DefaultJdbcTypeFactory}.
*
* @param operations must not be {@literal null}.
*/
public DefaultJdbcTypeFactory(JdbcOperations operations) {
this(operations, JdbcArrayColumns.DefaultSupport.INSTANCE);
}

/**
* Creates a new {@link DefaultJdbcTypeFactory}.
*
* @param operations must not be {@literal null}.
* @since 2.3
*/
public DefaultJdbcTypeFactory(JdbcOperations operations, 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) {

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 = jdbcType.getName();
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2021 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.relational.core.dialect.ArrayColumns;

/**
* {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC-specific functionality.
*
* @author Jens Schauder
* @since 2.3
*/
public interface JdbcArrayColumns extends ArrayColumns {

/*
* (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 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;

/*
* (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();
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2021 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.Dialect;

/**
* {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC specific functionality.
*
* @author Jens Schauder
* @since 2.3
*/
public interface JdbcDialect extends Dialect {

/**
* Returns the JDBC specific array support object that describes how array-typed columns are supported by this
* dialect.
*
* @return the JDBC specific array support object that describes how array-typed columns are supported by this
* dialect.
*/
@Override
JdbcArrayColumns getArraySupport();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2021 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.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
public JdbcArrayColumns getArraySupport() {
return ARRAY_COLUMNS;
}

static class JdbcPostgresArrayColumns extends PostgresArrayColumns implements JdbcArrayColumns {

@Override
public String getArrayTypeName(SQLType jdbcType) {

if (jdbcType == JDBCType.DOUBLE) {
return "FLOAT8";
}
if (jdbcType == JDBCType.REAL) {
return "FLOAT4";
}

return jdbcType.getName();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
Expand All @@ -31,7 +32,6 @@
import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.CustomConversions.StoreConversions;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.data.jdbc.core.convert.BasicJdbcConverter;
Expand All @@ -42,12 +42,12 @@
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.JdbcDb2Dialect;
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.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.dialect.Db2Dialect;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
Expand All @@ -66,7 +66,7 @@
@Configuration(proxyBeanMethods = false)
public class AbstractJdbcConfiguration implements ApplicationContextAware {

private static Logger LOG = LoggerFactory.getLogger(AbstractJdbcConfiguration.class);
private static final Logger LOG = LoggerFactory.getLogger(AbstractJdbcConfiguration.class);

private ApplicationContext applicationContext;

Expand Down Expand Up @@ -100,7 +100,10 @@ public JdbcMappingContext jdbcMappingContext(Optional<NamingStrategy> namingStra
public JdbcConverter jdbcConverter(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations,
@Lazy RelationResolver relationResolver, JdbcCustomConversions conversions, Dialect dialect) {

DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations());
JdbcArrayColumns arrayColumns = dialect instanceof JdbcDialect ? ((JdbcDialect) dialect).getArraySupport()
: JdbcArrayColumns.DefaultSupport.INSTANCE;
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(operations.getJdbcOperations(),
arrayColumns);

return new BasicJdbcConverter(mappingContext, relationResolver, conversions, jdbcTypeFactory,
dialect.getIdentifierProcessing());
Expand All @@ -120,7 +123,8 @@ public JdbcCustomConversions jdbcCustomConversions() {
try {

Dialect dialect = applicationContext.getBean(Dialect.class);
SimpleTypeHolder simpleTypeHolder = dialect.simpleTypes().isEmpty() ? JdbcSimpleTypes.HOLDER : new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
SimpleTypeHolder simpleTypeHolder = dialect.simpleTypes().isEmpty() ? JdbcSimpleTypes.HOLDER
: new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);

return new JdbcCustomConversions(
CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)), userConverters());
Expand Down
Loading