spring-projects / spring-projects/spring-data-rest
PagedResourcesAssembler not converting to PagedModel when using @Query
@odrotbohm is already working on this.
Since May 23, 2022.
- Dominant language
- Java
- Stars
- 958
- Forks
- 568
- PR merge metrics
- No merged PRs in 30d
Description
I'm trying to convert the result of a paged query to a PagedModel to generate the links automatically. Basically, the endpoint in the controller will return ResponseEntity<PagedModel<EntityModel<MyEntityProjection>>>, instead of returning Page<MyEntityProjection>.
For the client to get the list, usually the request will change from 'data.content' to 'data._embedded.myEntities'. I was able to make that conversion using PagedResourcesAssembler, but it only seems to work for custom queries using Query Lookup Strategies, but not with @Query.
Here's the code.
Repository:
@RepositoryRestResource
public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, Long>{
Page<MyEntityProjection> findAllByMyField(String value, Pageable pageable);
@Query("...")
Page<MyEntityProjection> findAllByMyFieldButWithQuery(String value, Pageable pageable);
@Query(value = "...", nativeQuery = true)
Page<MyEntityProjection> findAllByMyFieldButWithNativeQuery(String value, Pageable pageable);
}
The repository is doing the same query in three different ways. First with a query lookup strategy, second with JPQL and third with native query.
Controller:
@RepositoryRestController
public class MyEntityController {
private final MyEntityRepository repository;
private final PagedService pagedService;
@Autowired
public MyEntityController(...) {...}
@GetMapping(value = "/my-entities/find/{value}")
@ResponseBody
public ResponseEntity<PagedModel<EntityModel<MyEntityProjection>>> find(@PathVariable String value,
Pageable pageable, PagedResourcesAssembler<MyEntityProjection> assembler) {
Page<MyEntityProjection> myEntities = findByMyField(value, pageable);
return pagedService.buildPagedResponse(myEntities, MyEntityProjection.class, assembler); // works
}
... findButWithQuery(...) {
Page<MyEntityProjection> myEntities = findByMyFieldButWithQuery(value, pageable);
return pagedService.buildPagedResponse(myEntities, MyEntityProjection.class, assembler); // throws error here
}
... findByMyFieldButWithNativeQuery(...) {
Page<MyEntityProjection> myEntities = findByMyFieldButWithNativeQuery(value, pageable);
return pagedService.buildPagedResponse(myEntities, MyEntityProjection.class, assembler); // throws error here
}
}
The controller has three endpoints where each one calls one of the queries. It then uses the following service to convert a Page<MyEntityProjection> into a ResponseEntity<PagedModel<EntityModel<MyEntityProjection>>>.
Service:
@Service
public class PagedService {
@SuppressWarnings("unchecked")
public <P> ResponseEntity<PagedModel<EntityModel<P>>> buildPagedResponse(Page<P> pagedEntities,
Class<P> projectionClazz, PagedResourcesAssembler<P> assembler) {
PagedModel<EntityModel<P>> model = null;
if (pagedEntities.isEmpty()) {
model = (PagedModel<EntityModel<P>>) assembler.toEmptyModel(pagedEntities, projectionClazz);
} else {
model = assembler.toModel(pagedEntities);
}
return (ResponseEntity<PagedModel<EntityModel<P>>>) new ResponseEntity<PagedModel<EntityModel<P>>>(model,
HttpStatus.OK);
}
}
The service is just a generic implementation that converts to the desired response format.
When the endpoint tries to return the response, Jackson throws a NullPointerException. I presume that the assembler didn't build the '_embedded' part of the response. Here's the error:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: org.springframework.hateoas.PagedModel["_embedded"]->java.util.LinkedHashMap["tupleBackedMaps"]->java.util.ArrayList[0]->org.springframework.hateoas.EntityModel["content"])]
Shouldn't this conversion when using JPQL and native queries work in a similar way to the query lookup strategy?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.