diff --git a/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java b/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java index 8ecd363d71..445b6d9008 100644 --- a/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java +++ b/data-runtime/src/main/java/io/micronaut/data/runtime/query/internal/DefaultStoredQuery.java @@ -15,6 +15,9 @@ */ package io.micronaut.data.runtime.query.internal; +import io.micronaut.context.ApplicationContextProvider; +import io.micronaut.context.env.Environment; +import io.micronaut.context.env.PropertyPlaceholderResolver; import io.micronaut.core.annotation.AnnotationMetadata; import io.micronaut.core.annotation.AnnotationValue; import io.micronaut.core.annotation.Internal; @@ -39,6 +42,7 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -46,6 +50,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import static io.micronaut.data.intercept.annotation.DataMethod.META_MEMBER_LIMIT; @@ -88,6 +93,7 @@ public final class DefaultStoredQuery extends DefaultStoredDataOperation< private final Map> parameterExpressions; private final int limit; private final int offset; + private final Function stringsEnvResolverValueMapper; /** * The default constructor. @@ -132,6 +138,13 @@ public DefaultStoredQuery( HintsCapableRepository repositoryOperations) { super(method); + if (repositoryOperations instanceof ApplicationContextProvider applicationContextProvider) { + Environment environment = applicationContextProvider.getApplicationContext().getEnvironment(); + stringsEnvResolverValueMapper = createEnvResolverValueMapper(environment); + } else { + stringsEnvResolverValueMapper = null; + } + this.rootEntity = getRequiredRootEntity(method); this.annotationMetadata = method.getAnnotationMetadata(); this.isProcedure = dataMethodQuery.isTrue(DataMethodQuery.META_MEMBER_PROCEDURE); @@ -183,7 +196,7 @@ public DefaultStoredQuery( this.query = rawQueryString.orElse(query); } this.resultDataType = dataMethodQuery.enumValue(DataMethodQuery.META_MEMBER_RESULT_DATA_TYPE, DataType.class).orElse(DataType.OBJECT); - this.queryParts = dataMethodQuery.stringValues(DataMethodQuery.META_MEMBER_EXPANDABLE_QUERY); + this.queryParts = getQueryParts(dataMethodQuery, DataMethodQuery.META_MEMBER_EXPANDABLE_QUERY); //noinspection unchecked this.resultType = dataMethodQuery.classValue(DataMethodQuery.META_MEMBER_RESULT_TYPE) .map(type -> (Class) ReflectionUtils.getWrapperType(type)) @@ -480,4 +493,29 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(resultType, method); } + + private String[] getQueryParts(@NonNull AnnotationValue annotationValue, @NonNull String member) { + if (stringsEnvResolverValueMapper != null) { + return annotationValue.stringValues(member, stringsEnvResolverValueMapper); + } + return annotationValue.stringValues(member); + } + + private static Function createEnvResolverValueMapper(Environment environment) { + return o -> { + PropertyPlaceholderResolver resolver = environment.getPlaceholderResolver(); + if (o instanceof String[] values) { + String[] resolvedValues = Arrays.copyOf(values, values.length); + for (int i = 0; i < values.length; i++) { + String value = values[i]; + if (value.contains(resolver.getPrefix())) { + value = resolver.resolveRequiredPlaceholders(value); + } + resolvedValues[i] = value; + } + return resolvedValues; + } + return o; + }; + } } diff --git a/doc-examples/jdbc-example-java/src/main/java/example/CustomEntityRepository.java b/doc-examples/jdbc-example-java/src/main/java/example/CustomEntityRepository.java index 82a36c74fb..9041f437af 100644 --- a/doc-examples/jdbc-example-java/src/main/java/example/CustomEntityRepository.java +++ b/doc-examples/jdbc-example-java/src/main/java/example/CustomEntityRepository.java @@ -1,9 +1,31 @@ package example; +import io.micronaut.data.annotation.Query; import io.micronaut.data.jdbc.annotation.JdbcRepository; +import io.micronaut.data.model.Page; +import io.micronaut.data.model.Pageable; +import io.micronaut.data.model.Slice; import io.micronaut.data.model.query.builder.sql.Dialect; import io.micronaut.data.repository.CrudRepository; +import java.util.List; +import java.util.Set; + @JdbcRepository(dialect = Dialect.H2) public interface CustomEntityRepository extends CrudRepository { + Page findAll(Pageable pageable); + + Page findByNameIn(List names, Pageable pageable); + + @Query(value = "SELECT * FROM ${entity.prefix}entity WHERE name IN ('${entity.name}')", nativeQuery = true) + List findDataByEnvPropertyValue(); + + @Query(value = "SELECT id, '${entity.name}' AS name FROM ${entity.prefix}entity WHERE id IN (:id)", nativeQuery = true + ) + List findDataById(List id); + + @Query(value = "SELECT COUNT(*) FROM ${entity.prefix}entity WHERE name IN ('${entity.name}')", nativeQuery = true) + long countDataByEnvPropertyValue(); + + long countByIdIn(List ids); } diff --git a/doc-examples/jdbc-example-java/src/main/resources/application.yml b/doc-examples/jdbc-example-java/src/main/resources/application.yml index a0e33c09e5..d3d7cf8634 100644 --- a/doc-examples/jdbc-example-java/src/main/resources/application.yml +++ b/doc-examples/jdbc-example-java/src/main/resources/application.yml @@ -8,3 +8,4 @@ datasources: dialect: H2 entity: prefix: demo_ + name: Entity1 diff --git a/doc-examples/jdbc-example-java/src/test/java/example/CustomEntityRepositorySpec.java b/doc-examples/jdbc-example-java/src/test/java/example/CustomEntityRepositorySpec.java index ad08ecc77b..d7761785f2 100644 --- a/doc-examples/jdbc-example-java/src/test/java/example/CustomEntityRepositorySpec.java +++ b/doc-examples/jdbc-example-java/src/test/java/example/CustomEntityRepositorySpec.java @@ -1,16 +1,24 @@ package example; +import io.micronaut.context.annotation.Value; +import io.micronaut.data.model.Page; +import io.micronaut.data.model.Pageable; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.List; + @MicronautTest class CustomEntityRepositorySpec { @Inject CustomEntityRepository repository; + @Value("${entity.name}") + String entityName; + @Test void testSaveAndFind() { CustomEntity entity = repository.save(new CustomEntity(null, "Entity1")); @@ -19,6 +27,42 @@ void testSaveAndFind() { Assertions.assertEquals(entity.name(), found.name()); Assertions.assertEquals(1, repository.count()); Assertions.assertFalse(repository.findAll().isEmpty()); + + CustomEntity entity2 = repository.save(new CustomEntity(null, "Entity2")); + CustomEntity entity3 = repository.save(new CustomEntity(null, "Entity3")); + + List allNames = List.of("Entity1", "Entity2", "Entity3"); + List allIds = List.of(entity.id(), entity2.id(), entity3.id()); + + Page page = repository.findAll(Pageable.from(0, 2)); + Assertions.assertEquals(2, page.getSize()); + Assertions.assertEquals(3, page.getTotalSize()); + + page = repository.findByNameIn(allNames, Pageable.from(0, 2)); + Assertions.assertEquals(2, page.getSize()); + Assertions.assertEquals(2, page.getTotalPages()); + Assertions.assertEquals(3, page.getTotalSize()); + + Assertions.assertEquals(3, repository.countByIdIn(allIds)); + + page = repository.findByNameIn(List.of("Entity1", "Entity2"), + Pageable.from(0, 2)); + Assertions.assertEquals(2, page.getSize()); + Assertions.assertEquals(1, page.getTotalPages()); + Assertions.assertEquals(2, page.getTotalSize()); + + List customEntities = repository.findDataByEnvPropertyValue(); + Assertions.assertEquals(1, customEntities.size()); + Assertions.assertEquals(entityName, customEntities.get(0).name()); + + Assertions.assertEquals(1, repository.countDataByEnvPropertyValue()); + + customEntities = repository.findDataById(allIds); + Assertions.assertEquals(3, customEntities.size()); + for (CustomEntity customEntity : customEntities) { + Assertions.assertEquals(entityName, customEntity.name()); + } + repository.deleteAll(); } }