Skip to content

Commit 833e544

Browse files
committed
Add AOT support for Streamable wrapper support.
We now support streamable wrappers through AOT repositories. See #2175
1 parent 1ab242d commit 833e544

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/aot/JdbcCodeBlocks.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import org.jspecify.annotations.Nullable;
3333

3434
import org.springframework.core.annotation.MergedAnnotation;
35+
import org.springframework.core.convert.TypeDescriptor;
36+
import org.springframework.core.convert.support.DefaultConversionService;
3537
import org.springframework.data.domain.SliceImpl;
3638
import org.springframework.data.domain.Sort;
3739
import org.springframework.data.javapoet.LordOfTheStrings;
@@ -812,11 +814,22 @@ private CodeBlock select(ExecutionDecorator decorator, String rowMapper, String
812814
return builder.build();
813815
}
814816

815-
if (methodReturn.toClass().equals(Streamable.class)) {
817+
if (isStreamable(methodReturn)) {
816818
builder.addStatement(
817819
"return ($1T) $1T.of(($2T) convertMany($3L, %s))".formatted(dynamicProjection ? "$4L" : "$4T.class"),
818820
Streamable.class, Iterable.class, result, queryResultTypeRef);
821+
} else if (isStreamableWrapper(methodReturn) && canConvert(Streamable.class, methodReturn)) {
822+
823+
builder.addStatement(
824+
"$1T $2L = ($1T) convertMany($3L, %s)".formatted(dynamicProjection ? "$4L" : "$4T.class"), Iterable.class,
825+
context.localVariable("converted"), result, queryResultTypeRef);
826+
827+
builder.addStatement(
828+
"return ($1T) $2T.getSharedInstance().convert($3T.of($4L), $5T.valueOf($3T.class), $5T.valueOf($1T.class))",
829+
methodReturn.toClass(), DefaultConversionService.class, Streamable.class,
830+
context.localVariable("converted"), TypeDescriptor.class);
819831
} else {
832+
820833
builder.addStatement("return ($T) convertMany($L, %s)".formatted(dynamicProjection ? "$L" : "$T.class"),
821834
methodReturn.getTypeName(), result, queryResultTypeRef);
822835
}
@@ -843,6 +856,18 @@ private CodeBlock select(ExecutionDecorator decorator, String rowMapper, String
843856
return builder.build();
844857
}
845858

859+
private boolean canConvert(Class<?> from, MethodReturn methodReturn) {
860+
return DefaultConversionService.getSharedInstance().canConvert(from, methodReturn.toClass());
861+
}
862+
863+
private static boolean isStreamable(MethodReturn methodReturn) {
864+
return methodReturn.toClass().equals(Streamable.class);
865+
}
866+
867+
private static boolean isStreamableWrapper(MethodReturn methodReturn) {
868+
return !isStreamable(methodReturn) && Streamable.class.isAssignableFrom(methodReturn.toClass());
869+
}
870+
846871
public static boolean returnsModifying(Class<?> returnType) {
847872

848873
return ClassUtils.resolvePrimitiveIfNecessary(returnType) == Integer.class

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.ArrayList;
3131
import java.util.Arrays;
3232
import java.util.Collections;
33+
import java.util.Iterator;
3334
import java.util.List;
3435
import java.util.Objects;
3536
import java.util.Optional;
@@ -603,6 +604,16 @@ public void sliceByNameShouldReturnCorrectResult() {
603604
assertThat(slice.hasNext()).isTrue();
604605
}
605606

607+
@Test // GH-2175
608+
public void streamableWrapperByNameShouldReturnCorrectResult() {
609+
610+
repository.saveAll(Arrays.asList(new DummyEntity("a1"), new DummyEntity("a2"), new DummyEntity("a3")));
611+
612+
DummyEntities entities = repository.findEntitiesByNameContains("a", Limit.of(5));
613+
614+
assertThat(entities).hasSize(3);
615+
}
616+
606617
@Test // GH-935
607618
public void queryByOffsetDateTime() {
608619

@@ -1585,6 +1596,8 @@ public interface DummyEntityRepository
15851596

15861597
Slice<DummyEntity> findSliceByNameContains(String name, Pageable pageable);
15871598

1599+
DummyEntities findEntitiesByNameContains(String name, Limit limit);
1600+
15881601
@Query("SELECT * FROM DUMMY_ENTITY WHERE OFFSET_DATE_TIME > :threshhold")
15891602
List<DummyEntity> findByOffsetDateTime(@Param("threshhold") OffsetDateTime threshhold);
15901603

@@ -1620,6 +1633,7 @@ public interface DummyEntityRepository
16201633

16211634
@Query("SELECT * FROM DUMMY_ENTITY WHERE BYTES = :bytes")
16221635
List<DummyEntity> findByBytes(byte[] bytes);
1636+
16231637
}
16241638

16251639
public interface RootRepository extends ListCrudRepository<Root, Long> {
@@ -2002,6 +2016,20 @@ public boolean isNew() {
20022016
}
20032017
}
20042018

2019+
public static class DummyEntities implements Streamable<DummyEntity> {
2020+
2021+
private final Streamable<DummyEntity> delegate;
2022+
2023+
public DummyEntities(Streamable<DummyEntity> delegate) {
2024+
this.delegate = delegate;
2025+
}
2026+
2027+
@Override
2028+
public Iterator<DummyEntity> iterator() {
2029+
return delegate.iterator();
2030+
}
2031+
}
2032+
20052033
public static class DummyEntity {
20062034

20072035
@Id Long idProp;

0 commit comments

Comments
 (0)