Skip to content

Commit

Permalink
Allow for the repository definition to specify custom DTO and query t…
Browse files Browse the repository at this point in the history
…ypes (#2329)
  • Loading branch information
dstepanov authored Jun 27, 2023
1 parent e1fc763 commit a19b32f
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,18 @@ TypeRole[] typeRoles() default {
*/
boolean namedParameters() default true;

/**
* Custom data-transfer types that this repository supports.
* @return The DTO types
* @since 4.0.0
*/
Class<?>[] queryDtoTypes() default {};

/**
* Custom query return types that this repository supports.
* @return The query return types
* @since 4.0.0
*/
Class<?>[] queryReturnTypes() default {};

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.micronaut.data.document.annotation.DocumentProcessorRequired;
import io.micronaut.data.document.model.query.builder.MongoQueryBuilder;
import io.micronaut.data.mongodb.operations.MongoRepositoryOperations;
import org.bson.BsonDocument;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
Expand All @@ -38,7 +39,8 @@
queryBuilder = MongoQueryBuilder.class,
operations = MongoRepositoryOperations.class,
implicitQueries = true,
namedParameters = false
namedParameters = false,
queryDtoTypes = BsonDocument.class
)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.data.annotation.RepositoryConfiguration;
import io.micronaut.data.annotation.TypeRole;
import io.micronaut.data.intercept.DataInterceptor;
import io.micronaut.data.intercept.FindByIdInterceptor;
Expand All @@ -38,6 +39,7 @@
import io.micronaut.inject.ast.MethodElement;

import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Map;

/**
Expand Down Expand Up @@ -107,6 +109,10 @@ private boolean isCompatibleReturnType(@NonNull MatchContext matchContext) {
if (returnType == null) {
return false;
}
if (Arrays.stream(matchContext.getRepositoryClass().stringValues(RepositoryConfiguration.class, "queryReturnTypes"))
.anyMatch(type -> returnType.getName().equals(type))) {
return true;
}
if (!TypeUtils.isVoid(returnType)) {
return returnType.hasStereotype(Introspected.class) ||
returnType.isPrimitive() ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
package io.micronaut.data.processor.visitors.finders;

import io.micronaut.core.annotation.Internal;
import io.micronaut.data.annotation.RepositoryConfiguration;
import io.micronaut.data.processor.visitors.MethodMatchContext;
import io.micronaut.data.processor.visitors.finders.criteria.QueryCriteriaMethodMatch;

import java.util.Arrays;

/**
* List method matcher.
*
Expand All @@ -34,7 +37,8 @@ public ListMethodMatcher() {

@Override
protected MethodMatch match(MethodMatchContext matchContext, java.util.regex.Matcher matcher) {
if (TypeUtils.isContainerType(matchContext.getReturnType())) {
if (TypeUtils.isContainerType(matchContext.getReturnType()) || Arrays.stream(matchContext.getRepositoryClass().stringValues(RepositoryConfiguration.class, "queryReturnTypes"))
.anyMatch(type -> matchContext.getReturnType().getName().equals(type))) {
return new QueryCriteriaMethodMatch(matcher);
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.micronaut.core.util.StringUtils;
import io.micronaut.data.annotation.Join;
import io.micronaut.data.annotation.MappedEntity;
import io.micronaut.data.annotation.RepositoryConfiguration;
import io.micronaut.data.annotation.TypeRole;
import io.micronaut.data.intercept.DataInterceptor;
import io.micronaut.data.intercept.annotation.DataMethod;
Expand Down Expand Up @@ -174,10 +175,12 @@ protected MethodMatchInfo build(MethodMatchContext matchContext) {

boolean isDto = resultType != null
&& !TypeUtils.areTypesCompatible(resultType, queryResultType)
&& (isDtoType(resultType) || resultType.hasStereotype(Introspected.class) && queryResultType.hasStereotype(MappedEntity.class));
&& (isDtoType(matchContext.getRepositoryClass(), resultType) || resultType.hasStereotype(Introspected.class) && queryResultType.hasStereotype(MappedEntity.class));

ClassElement finalResultType = resultType;

if (isDto) {
if (!isDtoType(resultType)) {
if (!isDtoType(matchContext.getRepositoryClass(), resultType)) {
List<SourcePersistentProperty> dtoProjectionProperties = getDtoProjectionProperties(matchContext.getRootEntity(), resultType);
if (!dtoProjectionProperties.isEmpty()) {
Root<?> root = query.getRoots().iterator().next();
Expand All @@ -194,7 +197,8 @@ protected MethodMatchInfo build(MethodMatchContext matchContext) {
);
}
}
} else {
} else if (Arrays.stream(matchContext.getRepositoryClass().stringValues(RepositoryConfiguration.class, "queryReturnTypes"))
.noneMatch(type -> finalResultType.getName().equals(type))) {
if (resultType == null || (!resultType.isAssignable(void.class) && !resultType.isAssignable(Void.class))) {
if (resultType == null || TypeUtils.areTypesCompatible(resultType, queryResultType)) {
if (!queryResultType.isPrimitive() || resultType == null) {
Expand Down Expand Up @@ -269,8 +273,9 @@ protected MethodMatchInfo build(MethodMatchContext matchContext) {
.countQueryResult(countQueryResult);
}

private boolean isDtoType(ClassElement classElement) {
return classElement.getName().equals("org.bson.BsonDocument");
private boolean isDtoType(ClassElement repositoryElement, ClassElement classElement) {
return Arrays.stream(repositoryElement.stringValues(RepositoryConfiguration.class, "queryDtoTypes"))
.anyMatch(type -> classElement.getName().equals(type));
}

private List<SourcePersistentProperty> getDtoProjectionProperties(SourcePersistentEntity entity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,38 @@ import static io.micronaut.data.processor.visitors.TestUtils.*

class SqlParameterBindingSpec extends AbstractDataSpec {

void "test custom repository return type"() {
given:
def repository = buildRepository('test.SaleRepository', """
import io.micronaut.data.tck.entities.Sale;
import io.micronaut.data.model.query.builder.sql.SqlQueryBuilder;
import java.util.Map;
@Repository
@RepositoryConfiguration(queryBuilder=SqlQueryBuilder.class, implicitQueries = false, namedParameters = false, queryReturnTypes = MyReturn.class)
@io.micronaut.context.annotation.Executable
interface SaleRepository extends CrudRepository<Sale, Long> {
MyReturn findAllById(Long id);
MyReturn list(Long id);
}
class MyReturn {
}
""")
def findAllById = repository.getRequiredMethod("findAllById", Long)
def list = repository.getRequiredMethod("list", Long)

expect:"The repository compiles"
repository != null
getDataTypes(findAllById) == [DataType.LONG]
getDataTypes(list) == [DataType.LONG]
}

void "test update binding respects data type"() {
given:
def repository = buildRepository('test.SaleRepository', """
Expand Down

0 comments on commit a19b32f

Please sign in to comment.