当前位置: 首页 > 知识库问答 >
问题:

如何正确使用PagedResourcesAssembler从Spring数据?

万俟均
2023-03-14

我正在使用Spring 4.0.0. RELEASE,Spring数据共享1.7.0. M1,Spring Hateoas 0.8.0. RELEASE

我的资源是一个简单的POJO:

public class UserResource extends ResourceSupport { ... }

我的资源汇编程序将User对象转换为UserResources对象:

@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
    }
}

在我的UserController我想检索页面

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

这不会调用UserResourceAssembler,只返回User的内容,而不是我的自定义UserResource

返回单个资源有效:

@Autowired
UserResourceAssembler assembler;

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

PagedResourcesAssembler需要一些泛型参数,但是我不能使用t来调用(t),因为我不想转换我的页面

所以问题是:它是如何工作的?

我的WebMVC配置支持:

@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);
    }

    /* ... */
}
@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);
}

共有3个答案

和弘博
2023-03-14

替代方式

另一种方法是使用范围HTTP头(请参阅RFC 7233中的更多内容)。您可以通过以下方式定义HTTP头:

Range: resources=20-41

也就是说,你想从20到41(包括)获得资源。这种方式允许API的使用者接收精确定义的资源。

这只是另一种方式。范围通常与其他单位(如字节等)一起使用

推荐方式

如果你想使用分页,并且有真正适用的应用编程接口(包括超媒体/HATEOAS),那么我建议在你的网址中添加页面和页面大小。例如:

http://host.loc/articles?Page=1&PageSize=20

然后,您可以在BaseApiController中读取这些数据,并在所有请求中创建一些QueryFilter对象:

{
    var requestHelper = new RequestHelper(Request);

    int page = requestHelper.GetValueFromQueryString<int>("page");
    int pageSize = requestHelper.GetValueFromQueryString<int>("pagesize");

    var filter = new QueryFilter
    {
        Page = page != 0 ? page : DefaultPageNumber,
        PageSize = pageSize != 0 ? pageSize : DefaultPageSize
    };

    return filter;
}

api应该返回一些特殊集合,其中包含有关项目数量的信息。

public class ApiCollection<T>
{
    public ApiCollection()
    {
        Data = new List<T>();
    }

    public ApiCollection(int? totalItems, int? totalPages)
    {
        Data = new List<T>();
        TotalItems = totalItems;
        TotalPages = totalPages;
    }

    public IEnumerable<T> Data { get; set; }

    public int? TotalItems { get; set; }
    public int? TotalPages { get; set; }
}

模型类可以继承某些支持分页的类:

public abstract class ApiEntity
{
    public List<ApiLink> Links { get; set; }
}

public class ApiLink
{
    public ApiLink(string rel, string href)
    {
        Rel = rel;
        Href = href;
    }

    public string Href { get; set; }

    public string Rel { get; set; }
}
冯文彬
2023-03-14

我想将资源列表转换为页面。但当给它PagedResourcesSembler时,它正在吞噬内部链接。

这会让你的名单被翻页。

 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;
    }
}

分页资源提供资源汇编程序告诉分页资源使用它,它什么也不做,只是返回它,因为它已经是传递的资源列表。

    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);
    }
冯敏达
2023-03-14

您似乎已经找到了正确的使用方法,但是我想在这里详细介绍一些细节,以便其他人也能找到。在这个答案中,我也详细介绍了PagedResourceAssembler。

Spring HATEOAS附带了各种表示模型的基类,使创建带有链接的表示变得容易。开箱即用提供了三种类型的课程:

  • 资源-一个项目资源。有效地环绕一些捕获单个项目并用链接丰富它的DTO或实体。
  • Resources-一个集合资源,它可以是某些东西的集合,但通常是资源实例的集合。
  • PagedResources-Resources的扩展,用于捕获额外的分页信息,如总页数等。

所有这些类都源自ResourceSupport,它是Link实例的基本容器。

ResourceAssembler现在是将域对象或DTO转换为此类资源实例的缓解组件。这里的重要部分是,它将一个源对象转换为一个目标对象。

因此,PagedResourcesAssembler将获取一个Spring DataPage实例,并通过评估Page并创建必要的PageMetadata以及导航页面的prevnext链接,将其转换为PagedResources实例。默认情况下,它将使用一个普通的SimplePagedResourceAssembler(一个PRA的内部类)将页面的各个元素转换为嵌套的资源实例,这可能是这里最有趣的部分。

为了允许自定义,PRA有附加的toResources(...)方法,这些方法接受委托ResourceAssembler来处理单个项目。因此您最终会得到这样的结果:

 class UserResource extends ResourceSupport { … }

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

客户端代码现在看起来像这样:

 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);

从即将到来的Spring Data Commons 1.7 RC1(以及Spring HATEOAS 0.9过渡版)开始,prevnext链接将被生成为符合RFC6540的URI模板,以公开HandlerMethodArgumentResolver中为可分页排序配置的分页请求参数。

通过使用@EnableSpringDataWebSupport注释config类,可以简化上面显示的配置,这将使您摆脱所有显式bean声明。

 类似资料:
  • 问题内容: 我正在使用Spring 4.0.0.RELEASE,Spring Data Commons 1.7.0.M1,Spring Hateoas 0.8.0.RELEASE 我的资源是一个简单的POJO: 我的资源汇编器将User对象转换为UserResource对象: 在我的UserController内,我想从我的服务中检索,然后将其转换为. 这不会调用,只是User返回的内容而不是我的

  • 我有一个名为的couchbase bucket,其中包含将近2000个文档,我为此bucket创建了一个二级索引(idx_customer),以优化我的查询: 在couchbase查询监视器中执行时,查询命中索引。 但我认为,当从spring数据存储库播放时,它错过了索引。这主要是因为存储库中的排序和分页使我的查询在其他没有索引的查询中被翻译。 我使用此存储库中的spring数据在spring b

  • 问题内容: 我只想检索UserAccount类中的某些列,所以我有以下代码: 我得到了空值作为回报。但是,如果我注释掉setProjections,我将获得具有所有属性的用户。在这种情况下,如何正确使用setProjection? 问题答案: 它返回一个Object数组,因此代码应为:

  • 问题内容: 我不知道我在哪里错了:/。当我运行这段代码时,我得到的只是一个空白元素。我似乎无法让insertRule方法执行任何操作(甚至不会产生错误)。我想念什么吗? 问题答案: 这有点令人困惑,但是您的代码确实可以工作,只是您看不到返回的XML树中插入的规则。 为了验证您的代码是否有效,您可以执行两个测试: 运行上面的代码片段,您可以看到CSS规则确实适用。并且属性也在控制台中更改。 当浏览器

  • 问题内容: 如何使用从类路径中查找递归资源? 例如 在“目录”中查找所有资源:想象一下 不幸的是,这只会检索到恰好该“目录”。 所有资源都已命名(递归) 但这返回一个空。 还有一个额外的问题:与有什么不同? 问题答案: 没有办法递归搜索类路径。您需要知道资源的完整路径名才能以这种方式检索它。该资源可能位于文件系统中的目录中,也可能位于jar文件中,因此它不像执行“类路径”的目录列表那样简单。您将需

  • 问题内容: 我最近开始使用ScriptManager。我有一个通过JavaScript填充的ASP.NET DropDownList控件。但是,我正在使用事件验证。因此,如果我不使用下拉菜单中的“ RegisterForEventValidation”调用,则会遇到以下错误。我怎么知道在第二个参数中设置什么值(我有“值”)?我正在通过JavaScript填充下拉列表,因此我不知道后面的代码中包含哪