spring-projects / spring-projects/spring-data-couchbase

returnType must not be null! when working with projections

Open
#2,036 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
284
Forks
197
PR merge metrics
No merged PRs in 30d

Description

Hello, I am migrating from Spring Boot 2.2 (Spring Couchbase 3.x) to Spring Boot 2.7 (Spring Couchbase 4.x) and I am getting this exception:

java.lang.IllegalArgumentException: returnType must not be null!
	at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.31.jar:5.3.31]
	at org.springframework.data.couchbase.core.ExecutableFindByQueryOperationSupport$ExecutableFindByQuerySupport.as(ExecutableFindByQueryOperationSupport.java:124) ~[spring-data-couchbase-4.4.18.jar:4.4.18]
	at org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery.lambda$getExecutionToWrap$1(AbstractCouchbaseQuery.java:126) ~[spring-data-couchbase-4.4.18.jar:4.4.18]
	at org.springframework.data.couchbase.repository.query.CouchbaseQueryExecution$ResultProcessingExecution.execute(CouchbaseQueryExecution.java:84) ~[spring-data-couchbase-4.4.18.jar:4.4.18]
	at org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery.doExecute(AbstractCouchbaseQuery.java:95) ~[spring-data-couchbase-4.4.18.jar:4.4.18]
	at org.springframework.data.couchbase.repository.query.AbstractCouchbaseQueryBase.execute(AbstractCouchbaseQueryBase.java:133) ~[spring-data-couchbase-4.4.18.jar:4.4.18]
	at org.springframework.data.couchbase.repository.query.AbstractCouchbaseQueryBase.execute(AbstractCouchbaseQueryBase.java:113) ~[spring-data-couchbase-4.4.18.jar:4.4.18]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137) ~[spring-data-commons-2.7.18.jar:2.7.18]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121) ~[spring-data-commons-2.7.18.jar:2.7.18]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:160) ~[spring-data-commons-2.7.18.jar:2.7.18]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:139) ~[spring-data-commons-2.7.18.jar:2.7.18]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.31.jar:5.3.31]
	at org.springframework.data.couchbase.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:141) ~[spring-data-couchbase-4.4.18.jar:4.4.18]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.31.jar:5.3.31]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.31.jar:5.3.31]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.31.jar:5.3.31]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:241) ~[spring-aop-5.3.31.jar:5.3.31]
	at com.sun.proxy.$Proxy223.getDetails(Unknown Source) ~[?:?]
	at com.example.UserController.getDetails(UserController.java:32) ~[classes/:?]

User Document:

@EqualsAndHashCode(callSuper = true)
@Document
@Data
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@Valid
public class User extends ParentUser {
    private String firstName;
    private String lastName;
    private String address;
    private boolean isTest;
}

Projection Interface:

public final class Projections {

    @Projection(name = "names", types = {User.class})
    interface Names {
        String getFirstName();
        String getLastName();
    }
}

User Repository

public interface UserRepository extends PagingAndSortingRepository<User, String> { 

	    @Query("""
            #{#n1ql.selectEntity} WHERE #{#n1ql.filter}
            AND isTest = true
            """
    )
    List<Projections.Names> getDetails(@Param("isTest") boolean isTest);

}

User Controller

@RestController
@RequestMapping(value = "/users")
@RequiredArgsConstructor
public class UserController {
    private final UserRepository repository;

    @GetMapping(value = {"/find/test"}, produces = APPLICATION_JSON_VALUE)
    public Projections.Names getDetails(@RequestParam("isTest") final boolean isTest) {
        return repository.getDetails(isTest);
    }

Can projections still be used?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ExecutableFindByQueryOperationSupport.as and AbstractCouchbaseQuery, using the provided UserRepository query and Projections.Names projection as the reproduction. Trace how the repository method's List<Projections.Names> return type is processed, then verify the behavior with the supplied controller call. Done means establishing whether interface projections are supported in this version and documenting or correcting the resulting behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.