Skip to content

Convert @Query method Iterable parameters like findBy* query methods #1226

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 2 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>3.0.0-SNAPSHOT</version>
<version>3.0.0-1212-query-in-clause-with-enum-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>3.0.0-SNAPSHOT</version>
<version>3.0.0-1212-query-in-clause-with-enum-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>3.0.0-SNAPSHOT</version>
<version>3.0.0-1212-query-in-clause-with-enum-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>3.0.0-SNAPSHOT</version>
<version>3.0.0-1212-query-in-clause-with-enum-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,7 @@ public JdbcValue writeJdbcValue(@Nullable Object value, Class<?> columnType, int
return writeJdbcValue(value, columnType, JdbcUtil.jdbcTypeFor(sqlType));
}


/*
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.convert.JdbcConverter#writeValue(java.lang.Object, java.lang.Class, int)
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 the original author or authors.
* Copyright 2020-2022 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.
Expand All @@ -19,16 +19,20 @@

import java.lang.reflect.Constructor;
import java.sql.SQLType;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.converter.Converter;
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.RelationalParameters;
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
Expand All @@ -53,6 +57,7 @@
* @author Maciej Walkowiak
* @author Mark Paluch
* @author Hebert Coelho
* @author Chirag Tailor
* @since 2.0
*/
public class StringBasedJdbcQuery extends AbstractJdbcQuery {
Expand Down Expand Up @@ -149,11 +154,34 @@ private void convertAndAddParameter(MapSqlParameterSource parameters, Parameter

String parameterName = p.getName().orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED));

Class<?> parameterType = queryMethod.getParameters().getParameter(p.getIndex()).getType();
Class<?> conversionTargetType = JdbcColumnTypes.INSTANCE.resolvePrimitiveType(parameterType);
RelationalParameters.RelationalParameter parameter = queryMethod.getParameters().getParameter(p.getIndex());
ResolvableType resolvableType = parameter.getResolvableType();
Class<?> type = resolvableType.resolve();
Assert.notNull(type, "@Query parameter could not be resolved!");

JdbcValue jdbcValue = converter.writeJdbcValue(value, conversionTargetType,
JdbcUtil.targetSqlTypeFor(conversionTargetType));
JdbcValue jdbcValue;
if (value instanceof Iterable) {

List<Object> mapped = new ArrayList<>();
SQLType jdbcType = null;

Class<?> elementType = resolvableType.getGeneric(0).resolve();
Assert.notNull(elementType, "@Query Iterable parameter generic type could not be resolved!");
for (Object o : (Iterable<?>) value) {
JdbcValue elementJdbcValue = converter.writeJdbcValue(o, elementType,
JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(elementType)));
if (jdbcType == null) {
jdbcType = elementJdbcValue.getJdbcType();
}

mapped.add(elementJdbcValue.getValue());
}

jdbcValue = JdbcValue.of(mapped, jdbcType);
} else {
jdbcValue = converter.writeJdbcValue(value, type,
JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(type)));
}

SQLType jdbcType = jdbcValue.getJdbcType();
if (jdbcType == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2022 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.
Expand All @@ -23,6 +23,8 @@
import java.math.BigDecimal;
import java.sql.JDBCType;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -36,6 +38,7 @@
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.mapping.JdbcValue;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.jdbc.testing.AssumeFeatureTestExecutionListener;
import org.springframework.data.jdbc.testing.TestConfiguration;
Expand All @@ -50,6 +53,7 @@
*
* @author Jens Schauder
* @author Sanghyuk Jung
* @author Chirag Tailor
*/
@ContextConfiguration
@Transactional
Expand All @@ -69,18 +73,19 @@ Class<?> testClass() {
}

@Bean
EntityWithBooleanRepository repository() {
return factory.getRepository(EntityWithBooleanRepository.class);
EntityWithStringyBigDecimalRepository repository() {
return factory.getRepository(EntityWithStringyBigDecimalRepository.class);
}

@Bean
JdbcCustomConversions jdbcCustomConversions() {
return new JdbcCustomConversions(asList(StringToBigDecimalConverter.INSTANCE, BigDecimalToString.INSTANCE,
CustomIdReadingConverter.INSTANCE, CustomIdWritingConverter.INSTANCE));
CustomIdReadingConverter.INSTANCE, CustomIdWritingConverter.INSTANCE, DirectionToIntegerConverter.INSTANCE,
NumberToDirectionConverter.INSTANCE, IntegerToDirectionConverter.INSTANCE));
}
}

@Autowired EntityWithBooleanRepository repository;
@Autowired EntityWithStringyBigDecimalRepository repository;

/**
* In PostrgreSQL this fails if a simple converter like the following is used.
Expand Down Expand Up @@ -143,13 +148,52 @@ public void saveAndLoadAnEntityWithReference() {
});
}

interface EntityWithBooleanRepository extends CrudRepository<EntityWithStringyBigDecimal, CustomId> {}
@Test // GH-1212
void queryByEnumTypeIn() {

EntityWithStringyBigDecimal entityA = new EntityWithStringyBigDecimal();
entityA.direction = Direction.LEFT;
EntityWithStringyBigDecimal entityB = new EntityWithStringyBigDecimal();
entityB.direction = Direction.CENTER;
EntityWithStringyBigDecimal entityC = new EntityWithStringyBigDecimal();
entityC.direction = Direction.RIGHT;
repository.saveAll(asList(entityA, entityB, entityC));

assertThat(repository.findByEnumTypeIn(Set.of(Direction.LEFT, Direction.RIGHT)))
.extracting(entity -> entity.direction)
.containsExactlyInAnyOrder(Direction.LEFT, Direction.RIGHT);
}

@Test // GH-1212
void queryByEnumTypeEqual() {

EntityWithStringyBigDecimal entityA = new EntityWithStringyBigDecimal();
entityA.direction = Direction.LEFT;
EntityWithStringyBigDecimal entityB = new EntityWithStringyBigDecimal();
entityB.direction = Direction.CENTER;
EntityWithStringyBigDecimal entityC = new EntityWithStringyBigDecimal();
entityC.direction = Direction.RIGHT;
repository.saveAll(asList(entityA, entityB, entityC));

assertThat(repository.findByEnumTypeIn(Set.of(Direction.CENTER)))
.extracting(entity -> entity.direction)
.containsExactly(Direction.CENTER);
}

interface EntityWithStringyBigDecimalRepository extends CrudRepository<EntityWithStringyBigDecimal, CustomId> {
@Query("SELECT * FROM ENTITY_WITH_STRINGY_BIG_DECIMAL WHERE DIRECTION IN (:types)")
List<EntityWithStringyBigDecimal> findByEnumTypeIn(Set<Direction> types);

@Query("SELECT * FROM ENTITY_WITH_STRINGY_BIG_DECIMAL WHERE DIRECTION = :type")
List<EntityWithStringyBigDecimal> findByEnumType(Direction type);
}

private static class EntityWithStringyBigDecimal {

@Id CustomId id;
String stringyNumber;
String stringyNumber = "1.0";
OtherEntity reference;
Direction direction = Direction.CENTER;
}

private static class CustomId {
Expand All @@ -167,6 +211,10 @@ private static class OtherEntity {
Date created;
}

enum Direction {
LEFT, CENTER, RIGHT
}

@WritingConverter
enum StringToBigDecimalConverter implements Converter<String, JdbcValue> {

Expand Down Expand Up @@ -214,4 +262,55 @@ public CustomId convert(Number source) {
}
}

@WritingConverter
enum DirectionToIntegerConverter implements Converter<Direction, JdbcValue> {

INSTANCE;

@Override
public JdbcValue convert(Direction source) {

int integer = switch (source) {
case LEFT -> -1;
case CENTER -> 0;
case RIGHT -> 1;
};
return JdbcValue.of(integer, JDBCType.INTEGER);
}
}

@ReadingConverter // Needed for Oracle since the JDBC driver returns BigDecimal on read
enum NumberToDirectionConverter implements Converter<Number, Direction> {

INSTANCE;

@Override
public Direction convert(Number source) {
int sourceAsInt = source.intValue();
if (sourceAsInt == 0) {
return Direction.CENTER;
} else if (sourceAsInt < 0) {
return Direction.LEFT;
} else {
return Direction.RIGHT;
}
}
}

@ReadingConverter
enum IntegerToDirectionConverter implements Converter<Integer, Direction> {

INSTANCE;

@Override
public Direction convert(Integer source) {
if (source == 0) {
return Direction.CENTER;
} else if (source < 0) {
return Direction.LEFT;
} else {
return Direction.RIGHT;
}
}
}
}
Loading