Spring Boot JPA - 分页和排序

时间:2016-10-11 15:50:10

标签: java spring-mvc spring-boot spring-data-jpa spring-restcontroller

我正在尝试在Spring Boot中为我的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());
}

我花了几个小时试图完成这项工作,但我的想法已经不多了。感谢您的任何提示!

2 个答案:

答案 0 :(得分:3)

以您传递参数的方式更改方法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();
  }

答案 1 :(得分:0)

除了@ SEY_91之外,您还可以使用以下How to remove redundant Spring MVC method by providing POST-only @Valid?启发的解决方案,并在我的Spring Boot驱动的应用程序中使用了很长时间。

简而言之,这是一个注释控制器方法参数的注释:

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

}

我真的不记得了,但是Spring Framework中的某个resolve()方法应该存在。请注意,我使用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=43PageRequest{offset=43, pageNumber=43, pageSize=1, sort=null}
  • /?size=32PageRequest{offset=0, pageNumber=0, pageSize=32, sort=null}
  • /?page=22&size=32PageRequest{offset=704, pageNumber=22, pageSize=32, sort=null}