用JPA分页查询报错说:查询中至少提供了两个参数,但只有一个参数

    @GetMapping("/getByMaterialCode")
    List<PrimaryMaterialMain> getByMaterialCode(PrimaryMaterialMain materialCode) {
        PageRequest pageable = PageRequestUtil.buildPageRequest(0, 7, "");
        return primaryMaterialMainService.getByMaterialCode(materialCode, pageable).getContent();
    }

service:

public Page<PrimaryMaterialMain> getByMaterialCode(PrimaryMaterialMain materialCode, PageRequest pageable) {
        Specification<PrimaryMaterialMain> specification = SpecificationBuilder.buildSpecification(materialCode);
        Page<PrimaryMaterialMain> lists = primaryMaterialMainRepository.findAllByMaterialCodeContaining(specification, pageable);
        return lists;
    }

Dao:

Page<PrimaryMaterialMain> findByMaterialCodeContaining(Specification<PrimaryMaterialMain> specification, 
             PageRequest pageable);

然后就是报错:

clipboard.png

阅读 9.5k
1 个回答

题主,你好
emmm,首先小小的说哈不满,哈哈哈哈,其实就是看问题好歹也给个堆栈啊,这个就一个错误描述,精准度不够,所以我不敢保证我的方法管用,算是程序猿的一点直觉吧,下面是我个人看法的思路

如果不想看我自己的猜测过程,可以直接拉倒最下面看我的猜测,希望能帮到你,嘿嘿

整了个Spring-data-jpa的工程,从题主唯一给的错误描述出发(好吧,还是个截图。。。我得自己打出来),

At least 2 parameter(s) provided but only 1...

我首先认为这个错误应该不是题主自己抛出来的,所以直接在工程里搜索除了参数以外较完整的句子,就用这个了

parameter(s) provided but only

搜索,还真找个类,QueryParameterSetterFactory

clipboard.png

不过有两个地方,所以分别在两个地方打断点,自己创建了一个类似题主的例子,然后让其查询报错,最后发现是下面这个地方报错的

clipboard.png

可以看到,就是一个简单的断言,条件是parameterIndex < expressions.size(),从最后报错描述来对比看, expressions.size()显然是1,也就是这个Spring-data-jpa解析你提供的findByMaterialCodeContaining只需要一个查询条件的,但是奈何,题主给了两个条件,但是题主肯定说,第二个条件是分页条件PageRequest应该不算的啊,但是看起来是算上的了,所以继续沿着程序认为错误但是我们不认为错误的路径反向debug找,而且怀疑对象着重在PageRequest,当然就是要找为啥parameterIndex的大小是1的(debug看出来的),它是怎么生成的,从代码binding.getRequiredPosition()进入,最后发现它是取的类ParameterBinding的属性position,也就是说其实position此时值为2

static class ParameterBinding {

        private final @Nullable String name;
        private final @Nullable String expression;
        private final @Nullable Integer position;

那这个属性被赋值的地方只有一个,就是构造方法,并且是直接赋值的

clipboard.png

所以构造方法入口打断点,重新跑一次查询,不过这个断点加一个条件,因为我们不知道重新跑一边时,这个方法要执行几次,但是从最后条件出错的断言来看,position值是2,所以这个断点再加一个条件,防止其他的可能

clipboard.png

再跑一次之后,我们停下来了,这里是在初始化ParameterBinding并放入一个集合中,外面有一个JpaParameters的循环,从debug框中看到当前的循环的值parameterPageRequest,这个可是我们的怀疑对象

clipboard.png

加上,在是否初始化ParameterBinding之前还有个判断条件parameter.isBindable()

for (JpaParameter parameter : parameters) {

    if (parameter.isBindable()) {
        result.add(new ParameterBinding(++bindableParameterIndex));
    }
}

所以我们有理由怀疑,正因为这个判断条件通过了,导致了此时ParameterBinding生成,导致最终position值是2,所以我们在走进这个方法parameter.isBindable()去看看

@Override
public boolean isBindable() {
    return super.isBindable() || isTemporalParameter();
}

有两个判断方法,||连接,我们先看super.isBindable(),还是调用的另外的方法isSpecialParameter(),不过从方法的注释上看,貌似是我们要找的意思

/**
     * Returns whether the {@link Parameter} is to be bound to a query.
     *
     * @return
     */
public boolean isBindable() {
    return !isSpecialParameter();
}

再走入isSpecialParameter(),这个方法注释也说得很清楚了,special parameter特殊参数,感觉是那么回事了

/**
     * Returns whether the parameter is a special parameter.
     *
     * @return
     * @see #TYPES
     */
    public boolean isSpecialParameter() {
        return isDynamicProjectionParameter || TYPES.contains(parameter.getParameterType());
    }

第一个是isDynamicProjectionParameter是个booleandebug可以看到是false
第二个TYPES.contains(parameter.getParameterType()),我们看看TYPES

static final List<Class<?>> TYPES = Arrays.asList(Pageable.class, Sort.class);

emmm。。。貌似。。。有点明白了。。。当前你用的是PageRequest,不是Pageable,虽然PageRequest也是实现了Pageable接口,但是这里就是这么写了。。。也就是说方法里应该写Pageable作为方法参数,而不是实现类PageRequest

好吧。。。问题解决,顺便我也学一手(不过题主可能你还是没有解决问题,但是这是我能找到的大概问题所在吧。。。起码我改了之后,我这里的demo就可以了)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题