Spring 引导 JPA - 分页和排序
Spring Boot JPA - paging and sorting
我正在尝试在 Spring 引导中对我的 Spring Data JPA 存储库实施分页,但是当 运行 uni 测试时我遇到以下异常:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
...
有人可以指出我在这里缺少什么吗?这是我的存储库:
@Repository
public interface VenueRepository extends PagingAndSortingRepository<Venue, Long> {
public Page<Venue> findAll(Pageable pageable);
}
和控制器:
@RestController
@RequestMapping("/venues")
public class VenueController {
@Autowired
private VenueRepository venueRepo;
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Page<Venue>> getVenues(Pageable pageable) {
return new ResponseEntity<>(venueRepo.findAll(pageable), HttpStatus.OK);
}
}
最后是我的测试:
@Test
public void responseOkVenuesTest() throws Exception {
mvc.perform(get("/venues").accept(MediaType.APPLICATION_JSON_VALUE)).andExpect(status().isOk());
}
我花了几个小时试图完成这项工作,但 运行 没有想法。感谢您提供任何提示!
更改您的方法 getVenues
以传递参数来实例化 PageRequest
而不是传递 Pageable
:
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<Venue>> getVenues(int from,int to) {
return new ResponseEntity<>(
venueRepo.findAll((new PageRequest(from, to)), HttpStatus.OK).getContent();
}
除了@SEY_91's answer you might also like to use the following solution inspired with How to remove redundant Spring MVC method by providing POST-only @Valid? 并在我的 Spring 引导驱动应用程序中使用了很长时间。
简而言之,这里有一个注解控制器方法参数的注解:
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface PlainModelAttribute {
}
现在,只是一个方法处理器,它会扫描用 @PlainModelAttribute
:
注释的参数
public final class PlainModelAttributeMethodProcessor
extends ModelAttributeMethodProcessor {
private final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index;
private PlainModelAttributeMethodProcessor(final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index) {
super(true);
this.index = index;
}
public static HandlerMethodArgumentResolver plainModelAttributeMethodProcessor(final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index) {
return new PlainModelAttributeMethodProcessor(index);
}
@Override
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.hasParameterAnnotation(PlainModelAttribute.class) || super.supportsParameter(parameter);
}
@Override
protected Object createAttribute(final String attributeName, final MethodParameter parameter, final WebDataBinderFactory binderFactory,
final NativeWebRequest request) {
final TypeToken<?> typeToken = TypeToken.of(parameter.getGenericParameterType());
final Converter<? super NativeWebRequest, ?> converter = index.get(typeToken);
if ( converter == null ) {
throw new IllegalArgumentException("Cannot find a converter for " + typeToken.getType());
}
return converter.convert(request);
}
@Override
protected void bindRequestParameters(final WebDataBinder binder, final NativeWebRequest request) {
final HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if ( !isSafe(resolve(servletRequest.getMethod())) ) {
((ServletRequestDataBinder) binder).bind(servletRequest);
}
}
private static HttpMethod resolve(final String name) {
return HttpMethod.valueOf(name.toUpperCase());
}
private static boolean isSafe(final HttpMethod method)
throws UnsupportedOperationException {
switch ( method ) {
case GET:
case HEAD:
case OPTIONS:
return true;
case POST:
case PUT:
case PATCH:
case DELETE:
return false;
case TRACE:
throw new UnsupportedOperationException();
default:
throw new AssertionError(method);
}
}
}
我不太记得了,但是 resolve()
等效方法应该存在于 Spring Framework 的某个地方。请注意,我使用 Google Guava TypeToken 是为了让处理器与通用类型兼容(因为我在控制器中使用 IQuery<Foo>
和 IQuery<Bar>
等模型)。现在只需注册处理器:
@Configuration
@EnableWebMvc
public class MvcConfiguration
extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(createModelAttributeMethodProcessor());
}
private static HandlerMethodArgumentResolver createModelAttributeMethodProcessor() {
return plainModelAttributeMethodProcessor(ImmutableMap.of(pageableTypeToken, MvcConfiguration::toPageable));
}
private static final TypeToken<Pageable> pageableTypeToken = new TypeToken<Pageable>() {
};
private static Pageable toPageable(final WebRequest request) {
return new PageRequest(
ofNullable(request.getParameter("page")).map(Integer::parseInt).orElse(0),
ofNullable(request.getParameter("size")).map(Integer::parseInt).orElse(1)
);
}
}
这是对 Pageable
DTO 转换的 Web 请求,转换器必须注册为参数解析器。现在可以使用了:
@RestController
@RequestMapping("/")
public class Controller {
@RequestMapping(method = GET)
public String get(@PlainModelAttribute final Pageable pageable) {
return toStringHelper(pageable)
.add("offset", pageable.getOffset())
.add("pageNumber", pageable.getPageNumber())
.add("pageSize", pageable.getPageSize())
.add("sort", pageable.getSort())
.toString();
}
}
举几个例子:
/
⇒ PageRequest{offset=0, pageNumber=0, pageSize=1, sort=null}
/?page=43
⇒ PageRequest{offset=43, pageNumber=43, pageSize=1, sort=null}
/?size=32
⇒ PageRequest{offset=0, pageNumber=0, pageSize=32, sort=null}
/?page=22&size=32
⇒ PageRequest{offset=704, pageNumber=22, pageSize=32, sort=null}
我正在尝试在 Spring 引导中对我的 Spring Data JPA 存储库实施分页,但是当 运行 uni 测试时我遇到以下异常:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
...
有人可以指出我在这里缺少什么吗?这是我的存储库:
@Repository
public interface VenueRepository extends PagingAndSortingRepository<Venue, Long> {
public Page<Venue> findAll(Pageable pageable);
}
和控制器:
@RestController
@RequestMapping("/venues")
public class VenueController {
@Autowired
private VenueRepository venueRepo;
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Page<Venue>> getVenues(Pageable pageable) {
return new ResponseEntity<>(venueRepo.findAll(pageable), HttpStatus.OK);
}
}
最后是我的测试:
@Test
public void responseOkVenuesTest() throws Exception {
mvc.perform(get("/venues").accept(MediaType.APPLICATION_JSON_VALUE)).andExpect(status().isOk());
}
我花了几个小时试图完成这项工作,但 运行 没有想法。感谢您提供任何提示!
更改您的方法 getVenues
以传递参数来实例化 PageRequest
而不是传递 Pageable
:
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<Venue>> getVenues(int from,int to) {
return new ResponseEntity<>(
venueRepo.findAll((new PageRequest(from, to)), HttpStatus.OK).getContent();
}
除了@SEY_91's answer you might also like to use the following solution inspired with How to remove redundant Spring MVC method by providing POST-only @Valid? 并在我的 Spring 引导驱动应用程序中使用了很长时间。
简而言之,这里有一个注解控制器方法参数的注解:
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface PlainModelAttribute {
}
现在,只是一个方法处理器,它会扫描用 @PlainModelAttribute
:
public final class PlainModelAttributeMethodProcessor
extends ModelAttributeMethodProcessor {
private final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index;
private PlainModelAttributeMethodProcessor(final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index) {
super(true);
this.index = index;
}
public static HandlerMethodArgumentResolver plainModelAttributeMethodProcessor(final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index) {
return new PlainModelAttributeMethodProcessor(index);
}
@Override
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.hasParameterAnnotation(PlainModelAttribute.class) || super.supportsParameter(parameter);
}
@Override
protected Object createAttribute(final String attributeName, final MethodParameter parameter, final WebDataBinderFactory binderFactory,
final NativeWebRequest request) {
final TypeToken<?> typeToken = TypeToken.of(parameter.getGenericParameterType());
final Converter<? super NativeWebRequest, ?> converter = index.get(typeToken);
if ( converter == null ) {
throw new IllegalArgumentException("Cannot find a converter for " + typeToken.getType());
}
return converter.convert(request);
}
@Override
protected void bindRequestParameters(final WebDataBinder binder, final NativeWebRequest request) {
final HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if ( !isSafe(resolve(servletRequest.getMethod())) ) {
((ServletRequestDataBinder) binder).bind(servletRequest);
}
}
private static HttpMethod resolve(final String name) {
return HttpMethod.valueOf(name.toUpperCase());
}
private static boolean isSafe(final HttpMethod method)
throws UnsupportedOperationException {
switch ( method ) {
case GET:
case HEAD:
case OPTIONS:
return true;
case POST:
case PUT:
case PATCH:
case DELETE:
return false;
case TRACE:
throw new UnsupportedOperationException();
default:
throw new AssertionError(method);
}
}
}
我不太记得了,但是 resolve()
等效方法应该存在于 Spring Framework 的某个地方。请注意,我使用 Google Guava TypeToken 是为了让处理器与通用类型兼容(因为我在控制器中使用 IQuery<Foo>
和 IQuery<Bar>
等模型)。现在只需注册处理器:
@Configuration
@EnableWebMvc
public class MvcConfiguration
extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(createModelAttributeMethodProcessor());
}
private static HandlerMethodArgumentResolver createModelAttributeMethodProcessor() {
return plainModelAttributeMethodProcessor(ImmutableMap.of(pageableTypeToken, MvcConfiguration::toPageable));
}
private static final TypeToken<Pageable> pageableTypeToken = new TypeToken<Pageable>() {
};
private static Pageable toPageable(final WebRequest request) {
return new PageRequest(
ofNullable(request.getParameter("page")).map(Integer::parseInt).orElse(0),
ofNullable(request.getParameter("size")).map(Integer::parseInt).orElse(1)
);
}
}
这是对 Pageable
DTO 转换的 Web 请求,转换器必须注册为参数解析器。现在可以使用了:
@RestController
@RequestMapping("/")
public class Controller {
@RequestMapping(method = GET)
public String get(@PlainModelAttribute final Pageable pageable) {
return toStringHelper(pageable)
.add("offset", pageable.getOffset())
.add("pageNumber", pageable.getPageNumber())
.add("pageSize", pageable.getPageSize())
.add("sort", pageable.getSort())
.toString();
}
}
举几个例子:
/
⇒PageRequest{offset=0, pageNumber=0, pageSize=1, sort=null}
/?page=43
⇒PageRequest{offset=43, pageNumber=43, pageSize=1, sort=null}
/?size=32
⇒PageRequest{offset=0, pageNumber=0, pageSize=32, sort=null}
/?page=22&size=32
⇒PageRequest{offset=704, pageNumber=22, pageSize=32, sort=null}