spring-data-jpa: Annotated query with wildcard-appended named parameter does not handle null in 2.7.0
Context
We often create queries with optional filtering on parameters (ignore them when null) like so:
@Query("""
select j from Jedi j
where (:name is null or j.name = :name)
and (:desc is null or j.description like %:desc%)
""")
fun searchContains(
@Param("name") name: String?,
@Param("desc") desc: String?,
): List<Jedi>
As noted in Example 62 in https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query.advanced-like
the LIKE delimiter character (%) is recognized, and the query is transformed into a valid JPQL query (removing the %). Upon running the query, the parameter passed to the method call gets augmented with the previously recognized LIKE pattern.
This worked fine previously, but after upgrading til 2.7.0 it does not seem to work when such a named parameter is null, which it is intended to be when using that to implement optional filtering like this.
Problem
When the named parameter is null, the binded value is the literal string org.hibernate.jpa.TypedParameterValue@<number> augmented with the wildcard parameter, instead of just null.
I have prepared a demo app that showcases the issue: https://github.com/jonhakr/spring-data-jpa-null-optional-query The query above is generated as such:
select
jedi0_.id as id1_0_,
jedi0_.description as descript2_0_,
jedi0_.name as name3_0_
from
jedi jedi0_
where
(
? is null
or jedi0_.name=?
)
and (
? is null
or jedi0_.description like ?
)
When name is null it works fine, and the resulting bound parameter values are:
binding parameter [1] as [VARCHAR] - [null]
binding parameter [2] as [VARCHAR] - [null]
binding parameter [3] as [VARCHAR] - [%er%]
binding parameter [4] as [VARCHAR] - [%er%]
However, if the wildcard appended desc is null, the resulting bound parameter values are:
binding parameter [1] as [VARCHAR] - [Mace Windu]
binding parameter [2] as [VARCHAR] - [Mace Windu]
binding parameter [3] as [VARCHAR] - [%org.hibernate.jpa.TypedParameterValue@38d895e8%]
binding parameter [4] as [VARCHAR] - [%org.hibernate.jpa.TypedParameterValue@38d895e8%]
Simply setting spring-data-jpa to 2.6.4 makes this work fine again
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 10
- Comments: 19 (5 by maintainers)
Links to this issue
Commits related to this issue
- LikeParameterBinding should unwrap value if necessary Fix GH-2548 — committed to quaff/spring-data-jpa by quaff 2 years ago
- LikeParameterBinding should unwrap value if necessary Fix GH-2548 — committed to quaff/spring-data-jpa by quaff 2 years ago
- Properly handle null likes Properly handle null values with like or contains. Null values are wrapped with a special handler when interacting with Hibernate. However, this becomes an issue for queri... — committed to spring-projects/spring-data-jpa by gregturn 2 years ago
- Properly handle null values inside queries using LIKE or CONTAINS. Null values are wrapped with a special handler when interacting with Hibernate. However, this becomes an issue for queries when LIKE... — committed to spring-projects/spring-data-jpa by gregturn 2 years ago
- Properly handle null values inside queries using LIKE or CONTAINS. Null values are wrapped with a special handler when interacting with Hibernate. However, this becomes an issue for queries when LIKE... — committed to spring-projects/spring-data-jpa by gregturn 2 years ago
- Properly handle null values inside queries using LIKE or CONTAINS. Null values are wrapped with a special handler when interacting with Hibernate. However, this becomes an issue for queries when LIKE... — committed to spring-projects/spring-data-jpa by gregturn 2 years ago
- Unwrapping of TypedParameterValue, return null instead of empty string for string params. Fixes #2653 Related tickets #2548 — committed to yura2201/spring-data-jpa by yura2201 2 years ago
- Properly handle null mixed with LIKE. Resolves: #2653. Related: #2548, #2683, #2655, #2461, possibly #2544 — committed to spring-projects/spring-data-jpa by gregturn 2 years ago
- Properly handle null mixed with LIKE. Resolves: #2653. Related: #2548, #2683, #2655, #2461, possibly #2544 — committed to spring-projects/spring-data-jpa by gregturn 2 years ago
- Properly handle null mixed with LIKE. Resolves: #2653. Related: #2548, #2683, #2655, #2461, possibly #2544 — committed to spring-projects/spring-data-jpa by gregturn 2 years ago
Is there any plan for this issue ? (and the other related issues)
This was backported to
2.7.xas of f0216b0d.@gregturn seems like the change you made at 3d8b287e0e2129ec827b15613b44f31c19c65c61 addresses the original problem in 3.0.0-M5 release line. However, it is not obvious to me if this issue has to be handled in a different way in 2.7.x or will be backported to 2.7.x automatically during the next release.
I am maintaining an application that is stuck in 2.6.x due to this issue which is closed now, I tried to reproduce with spring-boot 2.7.3 and the issue still persists there. Any feedback regarding 2.7.x would be really helpful for the app maintainers who still use 2.6.x as noted by quaff here
We have the same issue with 2.7.4 and as workaround it helps to use
(:name IS NULL or p.name LIKE '%'||:name||'%' )instead of(:name IS NULL or p.name LIKE %:name%)(we use postgres, but other db I think it’s possible to use the appropriate concatenation func) @nemezmaksim
@jonhakr @gregturn the original issue was fixed however there still is a problem with null values. The demo app doesn’t show an example where the database contains null values. We use null values in ceratin columns which in turn does not return the full dataset we require. This worked correctly in versions under 2.7.0. Now on version 2.7.5 it does not.
Example
The null value previously was bound as null instead of wildcard like it is now.
Using the query from the demo app:
We change the data so it contains a null value in one of the records like so:
And when we run a query with parameters set to null the following parameters are bound:
This in turn returns only 2 of the records when all records should be returned:
However on version 2.6.4 before this issue appeared the null parameter is picked up by the is null condition and isn’t turned into a wildcard:
Which correctly returns all records.
I am still facing this issue with Spring Boot 3+ for the edge case of enum object nested within another object. Here’s an example:
This worked with 2.7.x
Strictly speaking, #2653 is a duplicate of this