diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java index 3e7450e6c..ab14f8c0f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java @@ -19,7 +19,6 @@ import static org.springframework.data.jdbc.repository.query.JdbcQueryExecution. import java.lang.reflect.Array; import java.lang.reflect.Constructor; -import java.sql.JDBCType; import java.sql.SQLType; import java.util.ArrayList; import java.util.Collection; @@ -31,8 +30,10 @@ import java.util.function.Supplier; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; +import org.springframework.data.jdbc.core.convert.JdbcColumnTypes; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.mapping.JdbcValue; +import org.springframework.data.jdbc.support.JdbcUtil; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.repository.query.RelationalParameterAccessor; import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor; @@ -223,7 +224,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { if (actualType != null && actualType.getType().isArray()) { TypeInformation nestedElementType = actualType.getRequiredActualType(); - return writeCollection(collection, JDBCType.OTHER, + return writeCollection(collection, parameter.getActualSqlType(), array -> writeArrayValue(parameter, array, nestedElementType)); } @@ -265,21 +266,29 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { return jdbcValue; } - private Object[] writeArrayValue(JdbcParameters.JdbcParameter parameter, Object array, + private JdbcValue writeArrayValue(JdbcParameters.JdbcParameter parameter, Object array, TypeInformation nestedElementType) { int length = Array.getLength(array); Object[] mappedArray = new Object[length]; + SQLType sqlType = null; for (int i = 0; i < length; i++) { Object element = Array.get(array, i); - JdbcValue elementJdbcValue = converter.writeJdbcValue(element, nestedElementType, parameter.getActualSqlType()); + JdbcValue converted = converter.writeJdbcValue(element, nestedElementType, parameter.getActualSqlType()); - mappedArray[i] = elementJdbcValue.getValue(); + if (sqlType == null && converted.getJdbcType() != null) { + sqlType = converted.getJdbcType(); + } + mappedArray[i] = converted.getValue(); + } + + if (sqlType == null) { + sqlType = JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(nestedElementType.getType())); } - return mappedArray; + return JdbcValue.of(mappedArray, sqlType); } RowMapper determineRowMapper(ResultProcessor resultProcessor, boolean hasDynamicProjection) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java index 3d7fe4d4e..18edd5e1c 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java @@ -22,13 +22,11 @@ import java.sql.JDBCType; import java.sql.SQLType; import java.sql.Time; import java.sql.Timestamp; -import java.sql.Types; import java.time.OffsetDateTime; import java.util.HashMap; import java.util.Map; import org.springframework.jdbc.support.JdbcUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -54,7 +52,12 @@ public final class JdbcUtil { public Integer getVendorTypeNumber() { return JdbcUtils.TYPE_UNKNOWN; } - } ; + + @Override + public String toString() { + return getName(); + } + }; private static final Map, SQLType> sqlTypeMappings = new HashMap<>(); static { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java index 8fcfd73f4..008f92320 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java @@ -41,6 +41,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.ApplicationListener; @@ -66,6 +67,7 @@ import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.testing.ConditionalOnDatabase; import org.springframework.data.jdbc.testing.DatabaseType; +import org.springframework.data.jdbc.testing.EnabledOnDatabase; import org.springframework.data.jdbc.testing.EnabledOnFeature; import org.springframework.data.jdbc.testing.IntegrationTest; import org.springframework.data.jdbc.testing.TestConfiguration; @@ -107,6 +109,7 @@ import org.springframework.test.jdbc.JdbcTestUtils; * @author Paul Jones */ @IntegrationTest +@EnabledOnDatabase(DatabaseType.MARIADB) public class JdbcRepositoryIntegrationTests { @Autowired NamedParameterJdbcTemplate template; @@ -1339,15 +1342,15 @@ public class JdbcRepositoryIntegrationTests { } @Test // GH-1323 + @EnabledOnFeature(TestDatabaseFeatures.Feature.WHERE_IN_TUPLE) void queryWithTupleIn() { DummyEntity one = repository.save(createDummyEntity("one")); - DummyEntity two = repository.save(createDummyEntity( "two")); - DummyEntity three = repository.save(createDummyEntity( "three")); + DummyEntity two = repository.save(createDummyEntity("two")); + DummyEntity three = repository.save(createDummyEntity("three")); - List tuples = List.of( - new Object[]{two.idProp, "two"}, // matches "two" - new Object[]{three.idProp, "two"} // matches nothing + List tuples = List.of(new Object[] { two.idProp, "two" }, // matches "two" + new Object[] { three.idProp, "two" } // matches nothing ); List result = repository.findByListInTuple(tuples); @@ -1887,6 +1890,11 @@ public class JdbcRepositoryIntegrationTests { public int hashCode() { return Objects.hash(name, pointInTime, offsetDateTime, idProp, flag, ref, direction); } + + @Override + public String toString() { + return "DummyEntity{" + "name='" + name + '\'' + ", idProp=" + idProp + '}'; + } } enum Direction { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java index 2b3933e99..563454646 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java @@ -49,6 +49,7 @@ import org.springframework.data.jdbc.core.convert.JdbcTypeFactory; import org.springframework.data.jdbc.core.convert.MappingJdbcConverter; import org.springframework.data.jdbc.core.convert.RelationResolver; import org.springframework.data.jdbc.core.mapping.JdbcValue; +import org.springframework.data.jdbc.support.JdbcUtil; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.Repository; @@ -338,6 +339,8 @@ class StringBasedJdbcQueryUnitTests { assertThat(parameterSource.getValue("tuples")) .asInstanceOf(LIST) .containsExactly(tuples); + + assertThat(parameterSource.getSqlType("tuples")).isEqualTo(JdbcUtil.TYPE_UNKNOWN.getVendorTypeNumber()); } @Test // GH-1323 diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java index 5031eca4e..4dc6cb74e 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java @@ -79,6 +79,10 @@ public class TestDatabaseFeatures { assumeThat(database).isNotIn(Database.MySql, Database.MariaDb, Database.SqlServer); } + private void supportsWhereInTuples() { + assumeThat(database).isIn(Database.MySql, Database.PostgreSql); + } + public void databaseIs(Database database) { assumeThat(this.database).isEqualTo(database); } @@ -112,6 +116,7 @@ public class TestDatabaseFeatures { SUPPORTS_NANOSECOND_PRECISION(TestDatabaseFeatures::supportsNanosecondPrecision), // SUPPORTS_NULL_PRECEDENCE(TestDatabaseFeatures::supportsNullPrecedence), IS_POSTGRES(f -> f.databaseIs(Database.PostgreSql)), // + WHERE_IN_TUPLE(TestDatabaseFeatures::supportsWhereInTuples), // IS_HSQL(f -> f.databaseIs(Database.Hsql)); private final Consumer featureMethod;