Search code examples
javaspringspring-bootspring-data-restspring-hateoas

Using a Spring Data Rest @Projection as a representation for a resource in a custom controller


Is there any way to use a @Projection interface as the default representation for a resource in SDR? Either through the SDR repositories or through a custom controller?

It used to be possible in a custom controller to do this by injecting a ProjectionFactory and using the createProjection method, but this has been broken by a recent Spring Data Rest update.

I would like to enforce a particular view on an entity, and the SDR projections seem like an ideal method for doing this, especially in the context of a HAL API, as opposed to writing hard DTO classes for a custom controller and mapping between them etc. Excerpt projections are not what I am after, as these only apply when looking at an associated resource.


Solution

  • To answer my own question, there's now a couple of easyish ways to do this.

    You can make SDR repository finders return projections by default:

    public interface PersonRepository extends PagingAndSortingRepository<Person,Long> {
    
        Set<PersonProjection> findByLastName(String lastName);
    
    }
    

    You can also selectively override responses that SDR would have handled for you by default by creating a custom Spring MVC controller with the @BasePathAwareController. You will need to inject the ProjectionFactory and possibly the PagedResourcesAssembler if you're planning on providing a paged response.

    @BasePathAwareController
    public class CustomPersonController {
    
    @Autowired
    private ProjectionFactory factory;
    
    @Autowired
    private PersonRepository personRepository;
    
    @Autowired
    private PagedResourcesAssembler<PersonProjection> assembler;
    
    @RequestMapping(value="/persons", method = RequestMethod.GET, produces = "application/hal+json")
    public ResponseEntity<?> getPeople(Pageable pageable) {
        Page<Person> people = personRepository.findAll(pageable);
        Page<PersonProjection> projected = people.map(l -> factory.createProjection(PersonProjection.class, l));
        PagedResources<Resource<PersonProjection>> resources = assembler.toResource(projected);
        return ResponseEntity.ok(resources);
    }