Skip to content

Commit

Permalink
Allow passing of tuples to repository query methods.
Browse files Browse the repository at this point in the history
Closes #1323
Original pull request: #1838
  • Loading branch information
schauder authored and mp911de committed Jul 22, 2024
1 parent 6504870 commit c71cbee
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

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;
Expand Down Expand Up @@ -45,6 +47,7 @@
import org.springframework.data.repository.query.SpelQueryContext;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.data.util.TypeUtils;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
Expand Down Expand Up @@ -204,23 +207,47 @@ private void convertAndAddParameter(MapSqlParameterSource parameters, Parameter
TypeInformation<?> typeInformation = parameter.getTypeInformation();

JdbcValue jdbcValue;
if (typeInformation.isCollectionLike() && value instanceof Collection<?>) {
if (typeInformation.isCollectionLike() //
&& value instanceof Collection<?> collectionValue//
) {
if ( typeInformation.getActualType().getType().isArray() ){

List<Object> mapped = new ArrayList<>();
SQLType jdbcType = null;
TypeInformation<?> arrayElementType = typeInformation.getActualType().getActualType();

TypeInformation<?> actualType = typeInformation.getRequiredActualType();
for (Object o : (Iterable<?>) value) {
JdbcValue elementJdbcValue = converter.writeJdbcValue(o, actualType, parameter.getActualSqlType());
if (jdbcType == null) {
jdbcType = elementJdbcValue.getJdbcType();
List<Object[]> mapped = new ArrayList<>();

for (Object array : collectionValue) {
int length = Array.getLength(array);
Object[] mappedArray = new Object[length];

for (int i = 0; i < length; i++) {
Object element = Array.get(array, i);
JdbcValue elementJdbcValue = converter.writeJdbcValue(element, arrayElementType, parameter.getActualSqlType());

mappedArray[i] = elementJdbcValue.getValue();
}
mapped.add(mappedArray);
}
jdbcValue = JdbcValue.of(mapped, JDBCType.OTHER);

mapped.add(elementJdbcValue.getValue());
}
} else {
List<Object> mapped = new ArrayList<>();
SQLType jdbcType = null;

TypeInformation<?> actualType = typeInformation.getRequiredActualType();
for (Object o : collectionValue) {
JdbcValue elementJdbcValue = converter.writeJdbcValue(o, actualType, parameter.getActualSqlType());
if (jdbcType == null) {
jdbcType = elementJdbcValue.getJdbcType();
}

jdbcValue = JdbcValue.of(mapped, jdbcType);
mapped.add(elementJdbcValue.getValue());
}

jdbcValue = JdbcValue.of(mapped, jdbcType);
}
} else {

SQLType sqlType = parameter.getSqlType();
jdbcValue = converter.writeJdbcValue(value, typeInformation, sqlType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,13 @@ public class JdbcRepositoryIntegrationTests {
@Autowired WithDelimitedColumnRepository withDelimitedColumnRepository;

private static DummyEntity createDummyEntity() {
return createDummyEntity("Entity Name");
}

private static DummyEntity createDummyEntity(String entityName) {

DummyEntity entity = new DummyEntity();
entity.setName("Entity Name");
entity.setName(entityName);

return entity;
}
Expand Down Expand Up @@ -1334,6 +1338,21 @@ void withDelimitedColumnTest() {
assertThat(inDatabase.get().getIdentifier()).isEqualTo("UR-123");
}

@Test // GH-1323
void queryWithTupleIn() {

DummyEntity one = repository.save(createDummyEntity("one"));
DummyEntity two = repository.save(createDummyEntity( "two"));
DummyEntity three = repository.save(createDummyEntity( "three"));

List<Object[]> tuples = List.of(
new Object[]{two.idProp, "two"}, // matches "two"
new Object[]{three.idProp, "two"} // matches nothing
);

repository.findByListInTuple(tuples);
}

private Root createRoot(String namePrefix) {

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

Optional<DummyAllArgsDto> findAllArgsDtoByIdProp(Long idProp);

@Query("SELECT * FROM DUMMY_ENTITY WHERE (ID_PROP, NAME) IN (:tuples)")
List<DummyEntity> findByListInTuple(List<Object[]> tuples);
}

interface RootRepository extends ListCrudRepository<Root, Long> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.sql.JDBCType;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
Expand Down Expand Up @@ -325,6 +326,33 @@ void appliesConverterToIterable() {
assertThat(sqlParameterSource.getValue("value")).isEqualTo("one");
}

@Test // GH-1323
void queryByListOfTuples() {

String[][] tuples = {new String[]{"Albert", "Einstein"}, new String[]{"Richard", "Feynman"}};

SqlParameterSource parameterSource = forMethod("findByListOfTuples", List.class) //
.withArguments(Arrays.asList(tuples))
.extractParameterSource();

assertThat(parameterSource.getValue("tuples"))
.asInstanceOf(LIST)
.containsExactly(tuples);
}

@Test // GH-1323
void queryByListOfConvertableTuples() {

SqlParameterSource parameterSource = forMethod("findByListOfTuples", List.class) //
.withCustomConverters(DirectionToIntegerConverter.INSTANCE) //
.withArguments(Arrays.asList(new Object[]{Direction.LEFT, "Einstein"}, new Object[]{Direction.RIGHT, "Feynman"}))
.extractParameterSource();

assertThat(parameterSource.getValue("tuples"))
.asInstanceOf(LIST)
.containsExactly(new Object[][]{new Object[]{-1, "Einstein"}, new Object[]{1, "Feynman"}});
}

QueryFixture forMethod(String name, Class... paramTypes) {
return new QueryFixture(createMethod(name, paramTypes));
}
Expand Down Expand Up @@ -450,6 +478,9 @@ interface MyRepository extends Repository<Object, Long> {

@Query("SELECT * FROM person WHERE lastname = $1")
Object unsupportedLimitQuery(@Param("lastname") String lastname, Limit limit);

@Query("select count(1) from person where (firstname, lastname) in (:tuples)")
Object findByListOfTuples(@Param("tuples") List<Object[]> tuples);
}

@Test // GH-619
Expand Down

0 comments on commit c71cbee

Please # to comment.