如何正确使用 Spring Data 中的 PagedResourcesAssembler?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21346387/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to correctly use PagedResourcesAssembler from Spring Data?
提问by Benjamin M
I'm using Spring 4.0.0.RELEASE, Spring Data Commons 1.7.0.M1, Spring Hateoas 0.8.0.RELEASE
我正在使用 Spring 4.0.0.RELEASE、Spring Data Commons 1.7.0.M1、Spring Hateoas 0.8.0.RELEASE
My resource is a simple POJO:
我的资源是一个简单的 POJO:
public class UserResource extends ResourceSupport { ... }
My resource assembler converts User objects to UserResource objects:
我的资源汇编器将 User 对象转换为 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
}
}
Inside my UserController I want to retrieve Page<User>
from my service and then convert it to PagedResources<UserResource>
using PagedResourcesAssembler
, like displayed here: https://stackoverflow.com/a/16794740/1321564
在我的 UserController 中,我想Page<User>
从我的服务中检索,然后将其转换为PagedResources<UserResource>
using PagedResourcesAssembler
,如下所示: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);
}
This doesn't call UserResourceAssembler
and simply the contents of User
are returned instead of my custom UserResource
.
这不会调用UserResourceAssembler
并且只是User
返回的内容而不是我的 custom UserResource
。
Returning a single resource works:
返回单个资源有效:
@Autowired
UserResourceAssembler assembler;
@RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(@PathVariable ObjectId id) throws NotFoundException {
return assembler.toResource(service.getById(id));
}
The PagedResourcesAssembler
wants some generic argument, but then I can't use T toResource(T)
, because I don't want to convert my Page<User>
to PagedResources<User>
, especially because User
is a POJO and no Resource.
ThePagedResourcesAssembler
想要一些通用参数,但后来我不能使用T toResource(T)
,因为我不想将 my 转换Page<User>
为PagedResources<User>
,尤其是因为User
是 POJO 而没有 Resource。
So the question is: How does it work?
所以问题是:它是如何工作的?
EDIT:
编辑:
My WebMvcConfigurationSupport:
我的 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);
}
回答by Oliver Drotbohm
You seem to have already found out about the proper way to use but I'd like to go into some of the details here a bit for others to find as well. I went into similar detail about PagedResourceAssembler
in this answer.
您似乎已经找到了正确的使用方法,但我想在这里详细介绍一些细节,以便其他人也可以找到。我PagedResourceAssembler
在这个答案中详细介绍了类似的细节。
Representation models
表示模型
Spring HATEOAS ships with a variety of base classes for representation models that make it easy to create representations equipped with links. There are three types of classes provided out of the box:
Spring HATEOAS 附带了各种表示模型的基类,可以轻松创建配备链接的表示。开箱即用提供了三种类型的类:
Resource
- an item resource. Effectively to wrap around some DTO or entity that captures a singleitem and enriches it with links.Resources
- a collection resource, that can be a collection of somethings but usually are a collection ofResource
instances.PagedResources
- an extension ofResources
that captures additional pagination information like the number of total pages etc.
Resource
- 物品资源。有效地环绕一些捕获单个项目并通过链接丰富它的DTO 或实体。Resources
- 集合资源,可以是某物的集合,但通常是Resource
实例的集合。PagedResources
- 一个扩展Resources
,捕获额外的分页信息,如总页数等。
All of these classes derive from ResourceSupport
, which is a basic container for Link
instances.
所有这些类都派生自ResourceSupport
,它是Link
实例的基本容器。
Resource assemblers
资源组装器
A ResourceAssembler
is now the mitigating component to convert your domain objects or DTOs into such resource instances. The important part here is, that it turns onesource object into onetarget object.
AResourceAssembler
现在是将域对象或 DTO 转换为此类资源实例的缓解组件。这里的重要部分是,它将一个源对象转换为一个目标对象。
So the PagedResourcesAssembler
will take a Spring Data Page
instance and transform it into a PagedResources
instance by evaluating the Page
and creating the necessary PageMetadata
as well as the prev
and next
links to navigate the pages. By default - and this is probably the interesting part here - it will use a plain SimplePagedResourceAssembler
(an inner class of PRA
) to transform the individual elements of the page into nested Resource
instances.
因此,PagedResourcesAssembler
将采取一个Spring数据Page
实例,并将它转换成PagedResources
通过对实例Page
和创造必要的PageMetadata
还有prev
和next
链接进行浏览的页面。默认情况下——这可能是这里有趣的部分——它将使用一个普通的SimplePagedResourceAssembler
( 的内部类PRA
)将页面的各个元素转换为嵌套Resource
实例。
To allow to customize this, PRA
has additional toResource(…)
methods that take a delegate ResourceAssembler
to process the individual items. So you end up with something like this:
为了允许对此进行自定义,PRA
还有其他toResource(…)
方法可以使用委托ResourceAssembler
来处理各个项目。所以你最终会得到这样的结果:
class UserResource extends ResourceSupport { … }
class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }
And the client code now looking something like this:
客户端代码现在看起来像这样:
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);
Outlook
外表
As of the upcoming Spring Data Commons 1.7 RC1 (and Spring HATEOAS 0.9 transitively) the prev
and next
links will be generated as RFC6540compliant URI templates to expose the pagination request parameters configured in the HandlerMethodArgumentResolvers
for Pageable
and Sort
.
从即将发布的 Spring Data Commons 1.7 RC1(和 Spring HATEOAS 0.9 传递)开始,prev
和next
链接将作为符合RFC6540 的URI 模板生成,以公开HandlerMethodArgumentResolvers
forPageable
和 中配置的分页请求参数Sort
。
The configuration you've shown above can be simplified by annotating the config class with @EnableSpringDataWebSupport
which would let you get rid off all the explicit bean declarations.
上面显示的配置可以通过注释配置类来简化,使用@EnableSpringDataWebSupport
它可以让您摆脱所有显式的 bean 声明。
回答by Arun Pratap Singh
I wanted to convert list of Resources to page. but when giving it PagedResourcesAssembler it was eating up the internal links.
我想将资源列表转换为页面。但是当给它 PagedResourcesAssembler 时,它正在吃掉内部链接。
This will get your List paged.
这将使您的列表分页。
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;
}
}
Paged resource Providing ResourceAssembler telling Paged resource to use it, which does nothing simply return's it back as it is already a resource list that is passed.
Paged 资源提供 ResourceAssembler 告诉 Paged 资源使用它,它什么都不做,只是将它返回,因为它已经是一个被传递的资源列表。
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);
}
回答by Miroslav Holec
ALTERNATIVE WAY
替代方式
Another way is use the Range HTTP header (read more in RFC 7233). You can define HTTP header this way:
另一种方法是使用 Range HTTP 标头(在RFC 7233 中阅读更多内容)。您可以通过以下方式定义 HTTP 标头:
Range: resources=20-41
That means, you want to get resource from 20 to 41 (including). This way allows consuments of API receive exactly defined resources.
这意味着,您希望从 20 到 41(包括)获取资源。这种方式允许 API 的使用者接收精确定义的资源。
It is just alternative way. Range is often used with another units (like bytes etc.)
这只是替代方式。范围通常与其他单位(如字节等)一起使用
RECOMMENDED WAY
推荐方式
If you wanna work with pagination and have really applicable API (hypermedia / HATEOAS included) then I recommend add Page and PageSize to your URL. Example:
如果您想使用分页并拥有真正适用的 API(包括超媒体/HATEOAS),那么我建议将 Page 和 PageSize 添加到您的 URL。例子:
http://host.loc/articles?Page=1&PageSize=20
Then, you can read this data in your BaseApiController and create some QueryFilter object in all your requests:
然后,您可以在 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;
}
Your api should returns some special collection with information about number of items.
您的 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; }
}
Your model classes can inherit some class with pagination support:
您的模型类可以继承一些具有分页支持的类:
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; }
}