Skip to content

Commit c71cbee

Browse files
schaudermp911de
authored andcommitted
Allow passing of tuples to repository query methods.
Closes #1323 Original pull request: #1838
1 parent 6504870 commit c71cbee

File tree

3 files changed

+92
-12
lines changed

3 files changed

+92
-12
lines changed

Diff for: spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java

+38-11
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import static org.springframework.data.jdbc.repository.query.JdbcQueryExecution.*;
1919

20+
import java.lang.reflect.Array;
2021
import java.lang.reflect.Constructor;
22+
import java.sql.JDBCType;
2123
import java.sql.SQLType;
2224
import java.util.ArrayList;
2325
import java.util.Collection;
@@ -45,6 +47,7 @@
4547
import org.springframework.data.repository.query.SpelQueryContext;
4648
import org.springframework.data.util.Lazy;
4749
import org.springframework.data.util.TypeInformation;
50+
import org.springframework.data.util.TypeUtils;
4851
import org.springframework.jdbc.core.ResultSetExtractor;
4952
import org.springframework.jdbc.core.RowMapper;
5053
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
@@ -204,23 +207,47 @@ private void convertAndAddParameter(MapSqlParameterSource parameters, Parameter
204207
TypeInformation<?> typeInformation = parameter.getTypeInformation();
205208

206209
JdbcValue jdbcValue;
207-
if (typeInformation.isCollectionLike() && value instanceof Collection<?>) {
210+
if (typeInformation.isCollectionLike() //
211+
&& value instanceof Collection<?> collectionValue//
212+
) {
213+
if ( typeInformation.getActualType().getType().isArray() ){
208214

209-
List<Object> mapped = new ArrayList<>();
210-
SQLType jdbcType = null;
215+
TypeInformation<?> arrayElementType = typeInformation.getActualType().getActualType();
211216

212-
TypeInformation<?> actualType = typeInformation.getRequiredActualType();
213-
for (Object o : (Iterable<?>) value) {
214-
JdbcValue elementJdbcValue = converter.writeJdbcValue(o, actualType, parameter.getActualSqlType());
215-
if (jdbcType == null) {
216-
jdbcType = elementJdbcValue.getJdbcType();
217+
List<Object[]> mapped = new ArrayList<>();
218+
219+
for (Object array : collectionValue) {
220+
int length = Array.getLength(array);
221+
Object[] mappedArray = new Object[length];
222+
223+
for (int i = 0; i < length; i++) {
224+
Object element = Array.get(array, i);
225+
JdbcValue elementJdbcValue = converter.writeJdbcValue(element, arrayElementType, parameter.getActualSqlType());
226+
227+
mappedArray[i] = elementJdbcValue.getValue();
228+
}
229+
mapped.add(mappedArray);
217230
}
231+
jdbcValue = JdbcValue.of(mapped, JDBCType.OTHER);
218232

219-
mapped.add(elementJdbcValue.getValue());
220-
}
233+
} else {
234+
List<Object> mapped = new ArrayList<>();
235+
SQLType jdbcType = null;
236+
237+
TypeInformation<?> actualType = typeInformation.getRequiredActualType();
238+
for (Object o : collectionValue) {
239+
JdbcValue elementJdbcValue = converter.writeJdbcValue(o, actualType, parameter.getActualSqlType());
240+
if (jdbcType == null) {
241+
jdbcType = elementJdbcValue.getJdbcType();
242+
}
221243

222-
jdbcValue = JdbcValue.of(mapped, jdbcType);
244+
mapped.add(elementJdbcValue.getValue());
245+
}
246+
247+
jdbcValue = JdbcValue.of(mapped, jdbcType);
248+
}
223249
} else {
250+
224251
SQLType sqlType = parameter.getSqlType();
225252
jdbcValue = converter.writeJdbcValue(value, typeInformation, sqlType);
226253
}

Diff for: spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,13 @@ public class JdbcRepositoryIntegrationTests {
117117
@Autowired WithDelimitedColumnRepository withDelimitedColumnRepository;
118118

119119
private static DummyEntity createDummyEntity() {
120+
return createDummyEntity("Entity Name");
121+
}
122+
123+
private static DummyEntity createDummyEntity(String entityName) {
120124

121125
DummyEntity entity = new DummyEntity();
122-
entity.setName("Entity Name");
126+
entity.setName(entityName);
123127

124128
return entity;
125129
}
@@ -1334,6 +1338,21 @@ void withDelimitedColumnTest() {
13341338
assertThat(inDatabase.get().getIdentifier()).isEqualTo("UR-123");
13351339
}
13361340

1341+
@Test // GH-1323
1342+
void queryWithTupleIn() {
1343+
1344+
DummyEntity one = repository.save(createDummyEntity("one"));
1345+
DummyEntity two = repository.save(createDummyEntity( "two"));
1346+
DummyEntity three = repository.save(createDummyEntity( "three"));
1347+
1348+
List<Object[]> tuples = List.of(
1349+
new Object[]{two.idProp, "two"}, // matches "two"
1350+
new Object[]{three.idProp, "two"} // matches nothing
1351+
);
1352+
1353+
repository.findByListInTuple(tuples);
1354+
}
1355+
13371356
private Root createRoot(String namePrefix) {
13381357

13391358
return new Root(null, namePrefix,
@@ -1461,6 +1480,9 @@ interface DummyEntityRepository extends CrudRepository<DummyEntity, Long>, Query
14611480
Optional<DummyDto> findDtoByIdProp(Long idProp);
14621481

14631482
Optional<DummyAllArgsDto> findAllArgsDtoByIdProp(Long idProp);
1483+
1484+
@Query("SELECT * FROM DUMMY_ENTITY WHERE (ID_PROP, NAME) IN (:tuples)")
1485+
List<DummyEntity> findByListInTuple(List<Object[]> tuples);
14641486
}
14651487

14661488
interface RootRepository extends ListCrudRepository<Root, Long> {

Diff for: spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java

+31
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.sql.JDBCType;
2323
import java.sql.ResultSet;
2424
import java.util.ArrayList;
25+
import java.util.Arrays;
2526
import java.util.Iterator;
2627
import java.util.List;
2728
import java.util.Properties;
@@ -325,6 +326,33 @@ void appliesConverterToIterable() {
325326
assertThat(sqlParameterSource.getValue("value")).isEqualTo("one");
326327
}
327328

329+
@Test // GH-1323
330+
void queryByListOfTuples() {
331+
332+
String[][] tuples = {new String[]{"Albert", "Einstein"}, new String[]{"Richard", "Feynman"}};
333+
334+
SqlParameterSource parameterSource = forMethod("findByListOfTuples", List.class) //
335+
.withArguments(Arrays.asList(tuples))
336+
.extractParameterSource();
337+
338+
assertThat(parameterSource.getValue("tuples"))
339+
.asInstanceOf(LIST)
340+
.containsExactly(tuples);
341+
}
342+
343+
@Test // GH-1323
344+
void queryByListOfConvertableTuples() {
345+
346+
SqlParameterSource parameterSource = forMethod("findByListOfTuples", List.class) //
347+
.withCustomConverters(DirectionToIntegerConverter.INSTANCE) //
348+
.withArguments(Arrays.asList(new Object[]{Direction.LEFT, "Einstein"}, new Object[]{Direction.RIGHT, "Feynman"}))
349+
.extractParameterSource();
350+
351+
assertThat(parameterSource.getValue("tuples"))
352+
.asInstanceOf(LIST)
353+
.containsExactly(new Object[][]{new Object[]{-1, "Einstein"}, new Object[]{1, "Feynman"}});
354+
}
355+
328356
QueryFixture forMethod(String name, Class... paramTypes) {
329357
return new QueryFixture(createMethod(name, paramTypes));
330358
}
@@ -450,6 +478,9 @@ interface MyRepository extends Repository<Object, Long> {
450478

451479
@Query("SELECT * FROM person WHERE lastname = $1")
452480
Object unsupportedLimitQuery(@Param("lastname") String lastname, Limit limit);
481+
482+
@Query("select count(1) from person where (firstname, lastname) in (:tuples)")
483+
Object findByListOfTuples(@Param("tuples") List<Object[]> tuples);
453484
}
454485

455486
@Test // GH-619

0 commit comments

Comments
 (0)