How to Pass Multiple Query Parameters in Spring Data JPA Native Queries
Problem
When I tried to pass multiple list parameters from a filter object to a native query in Spring Data JPA, I couldn’t figure out how to bind them correctly.
My repository method looked like this:
@Query( value = "SELECT e.empname, c.countryName, r.RegionName " + "FROM Employee e, Country c, Region r " + "WHERE e.countryId = c.countryId " + "AND e.employeeId IN (:empIds)", // How to pass empIds from request? nativeQuery = true)Collection<Object> findAllActiveUsersNative(CustomFilterRequest request, Pageable pageable);The CustomFilterRequest contains multiple lists:
public class CustomFilterRequest { private List<Long> empIds; private List<Long> countryIds; private List<Long> regionIds; // getters and setters}I needed to filter results by employee IDs, country IDs, and region IDs, but I didn’t know how to pass these list parameters to the native query.
Environment
- Java 17
- Spring Boot 3.x
- Spring Data JPA
- PostgreSQL database
What Happened?
I wanted to run a native SQL query that joins three tables (Employee, Country, Region) and filters by multiple lists of IDs. The native query approach is useful for complex joins where JPQL becomes cumbersome.

But when I tried to pass the CustomFilterRequest object directly, I realized I couldn’t access its properties in the SQL query using the standard :parameterName syntax.
How to Solve It?
I found two solutions that work.
Solution #1: Using SpEL Expressions
Spring Expression Language (SpEL) allows you to access properties from a single object parameter:
@Query( value = "SELECT e.empname, c.countryName, r.RegionName " + "FROM Employee e, Country c, Region r " + "WHERE e.countryId = c.countryId " + "AND c.regionId = r.regionId " + "AND e.employeeId IN (:#{#request.empIds}) " + "AND c.countryId IN (:#{#request.countryIds}) " + "AND r.regionId IN (:#{#request.regionIds})", nativeQuery = true)Collection<Object> findAllActiveUsersNative(CustomFilterRequest request, Pageable pageable);The key syntax is :#{#request.propertyName}:
#requestrefers to the method parameter namedrequest.empIdsaccesses theempIdsproperty of that object- The outer parentheses
(...)wrap the entire expression for theINclause
This approach keeps the method signature clean with just one parameter.
Solution #2: Using @Param Annotation
Alternatively, you can declare each parameter separately with @Param:
@Query( value = "SELECT e.empname, c.countryName, r.RegionName " + "FROM Employee e, Country c, Region r " + "WHERE e.countryId = c.countryId " + "AND c.regionId = r.regionId " + "AND e.employeeId IN :empIds " + "AND c.countryId IN :countryIds " + "AND r.regionId IN :regionIds", nativeQuery = true)Collection<Object> findAllActiveUsersNative( @Param("empIds") List<Long> empIds, @Param("countryIds") List<Long> countryIds, @Param("regionIds") List<Long> regionIds, Pageable pageable);Then in your service layer, you pass each list individually:
public Collection<Object> findActiveUsers(CustomFilterRequest request, Pageable pageable) { return repository.findAllActiveUsersNative( request.getEmpIds(), request.getCountryIds(), request.getRegionIds(), pageable );}The Reason
Both approaches work because Spring Data JPA handles parameter binding differently:
SpEL Approach:
- SpEL evaluates
:#{#request.empIds}at runtime - It extracts the list value from the request object
- The result is safely bound to the SQL query
- This prevents SQL injection by using parameterized queries
@Param Approach:
- Each
@Param("name")annotation explicitly maps a method parameter to a query placeholder - Spring Data JPA binds each parameter by name
- More explicit but requires unpacking the request object in the service layer
Visual Flow
Here’s how the parameter binding works:
┌─────────────────────────┐│ CustomFilterRequest ││ ┌─────────────────┐ ││ │ empIds │────┼──→ :#{#request.empIds}│ │ countryIds │────┼──→ :#{#request.countryIds}│ │ regionIds │────┼──→ :#{#request.regionIds}│ └─────────────────┘ │└─────────────────────────┘ │ ▼┌─────────────────────────┐│ Repository Method ││ @Query(nativeQuery=true)│ parameter binding │└─────────────────────────┘ │ ▼┌─────────────────────────┐│ Native SQL Execution ││ SELECT ... WHERE ... ││ AND empId IN (?) ││ AND countryId IN (?) ││ AND regionId IN (?) │└─────────────────────────┘Common Mistakes
When using SpEL, I made these mistakes:
- Missing parentheses around IN clause:
// Wrong - causes syntax errorAND e.employeeId IN :#{#request.empIds}
// Correct - wrap in parenthesesAND e.employeeId IN (:#{#request.empIds})- Parameter name mismatch:
// If method param is named "request"// Use #request in SpEL, not #filter or #req(:#{#request.empIds}) // Correct(:#{#filter.empIds}) // Wrong - no matching param- Mixing SpEL and @Param inconsistently:
Don’t use both approaches in the same query. Pick one style and stick with it.
Summary
In this post, I showed two ways to pass multiple query parameters in Spring Data JPA native queries. Use SpEL expressions (:#{#request.propertyName}) when you want a clean method signature with a single object parameter. Use @Param annotations when you prefer explicit parameter binding with individual method parameters. Both approaches work correctly with list parameters and prevent SQL injection.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments