web-dev-qa-db-fra.com

Comment utiliser correctement PagedResourcesAssembler à partir de Spring Data?

J'utilise Spring 4.0.0.RELEASE, Spring Data Commons 1.7.0.M1, Spring Hateoas 0.8.0.RELEASE

Ma ressource est un simple POJO:

public class UserResource extends ResourceSupport { ... }

Mon assembleur de ressources convertit les objets User en objets UserResource:

@Component
public class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { 
    public UserResourceAssembler() {
        super(UserController.class, UserResource.class);
    }

    @Override
    public UserResource toResource(User entity) {
        // map User to UserResource
    }
}

Dans mon UserController, je veux récupérer Page<User> De mon service, puis le convertir en PagedResources<UserResource> En utilisant PagedResourcesAssembler, comme affiché ici: https://stackoverflow.com/a/16794740/1321564

@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler assembler) {
    Page<User> u = service.get(p)
    return assembler.toResource(u);
}

Cela n'appelle pas UserResourceAssembler et simplement le contenu de User est retourné à la place de mon UserResource personnalisé.

Le retour d'une seule ressource fonctionne:

@Autowired
UserResourceAssembler assembler;

@RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(@PathVariable ObjectId id) throws NotFoundException {
    return assembler.toResource(service.getById(id));
}

PagedResourcesAssembler veut un argument générique, mais je ne peux pas utiliser T toResource(T), car je ne veux pas convertir mon Page<User> En PagedResources<User>, surtout parce que User est un POJO et aucune ressource.

La question est donc: comment ça marche?

ÉDITER:

Mon WebMvcConfigurationSupport:

@Configuration
@ComponentScan
@EnableHypermediaSupport
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(pageableResolver());
        argumentResolvers.add(sortResolver());
        argumentResolvers.add(pagedResourcesAssemblerArgumentResolver());
    }

    @Bean
    public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
        return new HateoasPageableHandlerMethodArgumentResolver(sortResolver());
    }

    @Bean
    public HateoasSortHandlerMethodArgumentResolver sortResolver() {
        return new HateoasSortHandlerMethodArgumentResolver();
    }

    @Bean
    public PagedResourcesAssembler<?> pagedResourcesAssembler() {
        return new PagedResourcesAssembler<Object>(pageableResolver(), null);
    }

    @Bean
    public PagedResourcesAssemblerArgumentResolver pagedResourcesAssemblerArgumentResolver() {
        return new PagedResourcesAssemblerArgumentResolver(pageableResolver(), null);
    }

    /* ... */
}

SOLUTION:

@Autowired
UserResourceAssembler assembler;

@RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(@PageableDefault Pageable p, PagedResourcesAssembler pagedAssembler) {
    Page<User> u = service.get(p)
    return pagedAssembler.toResource(u, assembler);
}
34
Benjamin M

Vous semblez avoir déjà découvert la bonne façon de l'utiliser, mais j'aimerais entrer un peu dans certains détails ici pour que d'autres les trouvent également. Je suis entré dans les détails similaires sur PagedResourceAssembler dans cette réponse .

Modèles de représentation

Spring HATEOAS est livré avec une variété de classes de base pour les modèles de représentation qui facilitent la création de représentations équipées de liens. Il existe trois types de classes prêtes à l'emploi:

  • Resource - une ressource d'élément. Pour envelopper efficacement un DTO ou une entité qui capture un élément unique et l'enrichit de liens.
  • Resources - une ressource de collection, qui peut être une collection de quelque chose mais qui est généralement une collection d'instances de Resource.
  • PagedResources - une extension de Resources qui capture des informations de pagination supplémentaires comme le nombre total de pages, etc.

Toutes ces classes dérivent de ResourceSupport, qui est un conteneur de base pour les instances de Link.

Assembleurs de ressources

Un ResourceAssembler est désormais le composant atténuant pour convertir vos objets de domaine ou DTO en de telles instances de ressources. La partie importante ici est qu'elle transforme un objet source en un objet cible.

Ainsi, le PagedResourcesAssembler prendra une instance de Spring Data Page et la transformera en une instance de PagedResources en évaluant le Page et en créant le PageMetadata nécessaire ainsi que le prev et next liens pour naviguer dans les pages. Par défaut - et c'est probablement la partie intéressante ici - il utilisera une simple SimplePagedResourceAssembler (une classe interne de PRA) pour transformer les éléments individuels de la page en instances imbriquées Resource.

Pour permettre de personnaliser cela, PRA a des méthodes toResource(…) supplémentaires qui prennent un délégué ResourceAssembler pour traiter les éléments individuels. Vous vous retrouvez donc avec quelque chose comme ceci:

 class UserResource extends ResourceSupport { … }

 class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }

Et le code client ressemble maintenant à ceci:

 PagedResourcesAssembler<User> parAssembler = … // obtain via DI
 UserResourceAssembler userResourceAssembler = … // obtain via DI

 Page<User> users = userRepository.findAll(new PageRequest(0, 10));

 // Tell PAR to use the user assembler for individual items.
 PagedResources<UserResource> pagedUserResource = parAssembler.toResource(
   users, userResourceAssembler);

Perspective

À partir du prochain Spring Data Commons 1.7 RC1 (et Spring HATEOAS 0.9 de manière transitoire), les liens prev et next seront générés sous la forme de modèles d'URI conformes RFC654 pour exposer les paramètres de demande de pagination configuré dans HandlerMethodArgumentResolvers pour Pageable et Sort.

La configuration que vous avez montrée ci-dessus peut être simplifiée en annotant la classe config avec @EnableSpringDataWebSupport qui vous permettrait de vous débarrasser de toutes les déclarations explicites du bean.

70
Oliver Drotbohm

Je voulais convertir la liste des ressources en page. mais en lui donnant PagedResourcesAssembler, il mangeait les liens internes.

Cela permettra de paginer votre liste.

 public class JobExecutionInfoResource extends ResourceSupport {
    private final JobExecutionInfo jobExecution;

    public JobExecutionInfoResource(final JobExecutionInfo jobExecution) {
        this.jobExecution = jobExecution;        
        add(ControllerLinkBuilder.linkTo(methodOn(JobsMonitorController.class).get(jobExecution.getId())).withSelfRel()); // add your own links.          
    }

    public JobExecutionInfo getJobExecution() {
        return jobExecution;
    }
}

Ressource paginée Fournir ResourceAssembler indiquant à la ressource paginée de l'utiliser, ce qui ne fait rien simplement la renvoyer car c'est déjà une liste de ressources qui est passée.

    private final PagedResourcesAssembler<JobExecutionInfoResource> jobExecutionInfoResourcePagedResourcesAssembler;
    public static final PageRequest DEFAULT_PAGE_REQUEST = new PageRequest(0, 20);
    public static final ResourceAssembler<JobExecutionInfoResource, JobExecutionInfoResource> SIMPLE_ASSEMBLER = entity -> entity;

@GetMapping("/{clientCode}/{propertyCode}/summary")
    public PagedResources<JobExecutionInfoResource> getJobsSummary(@PathVariable String clientCode, @PathVariable String propertyCode,
                                                                   @RequestParam(required = false) String exitStatus,
                                                                   @RequestParam(required = false) String jobName,
                                                                   Pageable pageRequest) {
        List<JobExecutionInfoResource> listOfResources = // your code to generate the list of resource;
        int totalCount = 10// some code to get total count;
        Link selfLink = linkTo(methodOn(JobsMonitorController.class).getJobsSummary(clientCode, propertyCode, exitStatus, jobName, DEFAULT_PAGE_REQUEST)).withSelfRel();
        Page<JobExecutionInfoResource> page = new PageImpl<>(jobExecutions, pageRequest, totalCount);
        return jobExecutionInfoResourcePagedResourcesAssembler.toResource(page, SIMPLE_ASSEMBLER, selfLink);
    }
0