Reputation: 63
Hi, I have an error and i dont know how to fix it. I see other similar issues here, but they haven't been useful to me. So, I hope you could help me.
I have this DAO:
public interface GoalDao extends PagingAndSortingRepository<Goal, Long> {
Slice<Goal> findByCompanyId(Long companyId, PageRequest of);
}
And this method in my service:
@Override
@Transactional(readOnly = true)
public Block<Goal> findCompanyGoals(Long userId, Long companyId, int page, int size)
throws InstanceNotFoundException, PermissionException {
Company company = permissionChecker.checkCompanyExistsAndBelongsToUser(companyId, userId);
Slice<Goal> slice = goalDao.findByCompanyIdOrderByIdDesc(company.getId(), PageRequest.of(page, size));
return new Block<>(slice.getContent(), slice.hasNext());
}
So, this line:
Slice<Goal> slice = goalDao.findByCompanyIdOrderByIdDesc(company.getId(), PageRequest.of(page, size));
It is throwing me the following error:
testException = org.springframework.dao.InvalidDataAccessApiUsageException: At least 2 parameter(s) provided but only 1 parameter(s) present in query.; nested exception is java.lang.IllegalArgumentException: At least 2 parameter(s) provided but only 1 parameter(s) present in query.
Thank you.
Upvotes: 0
Views: 4632
Reputation: 1217
To add more information to @Bisha's answer : It doesn't work with PageRequest because Spring accepts only two types (in a method signature) to apply sorting and pagination : Pageable
& Sort
.
Pageable
and Sort
are considered special parameters that are handled diffrently by Spring (It won't try to bind them to the query).
In your question's example, Spring treats your PageRequest
method parameter (in the method signature) as a normal method parameter (even if it implements Pageable) and tries to bind it to the final query hence the error message.
You can see where the check is done in Spring's source code (spring-data-jpa:2.4.10
and spring-data-commons:2.4.10
):
In org.springframework.data.jpa.repository.query.ParameterBinderFactory
, Spring checks if a parameter should be bound to the query by calling parameter.isBindable
:
private static List<ParameterBinding> getBindings(JpaParameters parameters) {
List<ParameterBinding> result = new ArrayList<>();
int bindableParameterIndex = 0;
for (JpaParameter parameter : parameters) {
if (parameter.isBindable()) {
result.add(new ParameterBinding(++bindableParameterIndex));
}
}
return result;
}
isBindable
in (org.springframework.data.jpa.repository.query.JpaParameters
) will ultimately call isBindable
in org.springframework.data.jpa.repository.query.Parameter
:
public boolean isBindable() {
return !isSpecialParameter();
}
isSpecialParameter
will return false
when the parameter type is PageRequest
which means isBindable
will true
and Spring will try to bind the parameter to the query instead of using it for pagination.
isSpecialParameter
will return false
because (in your case) the list TYPES
doesn't contain the class PageRequest
:
public boolean isSpecialParameter() {
return isDynamicProjectionParameter || TYPES.contains(parameter.getParameterType());
}
TYPES
is initialized in a static block in org.springframework.data.repository.query.Parameter
and has (in your case) only two values Pageable
and Sort
:
static {
List<Class<?>> types = new ArrayList<>(Arrays.asList(Pageable.class, Sort.class));
// consider Kotlin Coroutines Continuation a special parameter. That parameter is synthetic and should not get
// bound to any query.
ClassUtils.ifPresent("kotlin.coroutines.Continuation", Parameter.class.getClassLoader(), types::add);
TYPES = Collections.unmodifiableList(types);
}
Upvotes: 3
Reputation: 56
In your GoalDao try using Pageable instead of PageRequest. So it will look like this:
public interface GoalDao extends PagingAndSortingRepository<Goal, Long> {
Slice<Goal> findByCompanyId(Long companyId, Pageable pageable);
}
Not sure why, but it worked for me and I hope it will help you too.
Upvotes: 4