首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >名为参数绑定值解析的Spring 2@查询在从1.5升级到

名为参数绑定值解析的Spring 2@查询在从1.5升级到
EN

Stack Overflow用户
提问于 2019-04-03 12:36:28
回答 1查看 5.2K关注 0票数 1

我们有以下使用SpringBoot 1.5的工作查询:

代码语言:javascript
复制
@Query(value = "SELECT DISTINCT c FROM Customer c INNER JOIN c.industry i WHERE " +
            "c.role IN :roleFilter " +
            "AND (:#{#industryFilter.size()} = 1 OR i.id IN :industryFilter) " +
            "AND (:searchString IS NULL " +
            "OR CONCAT_WS(' ', c.name, c.name2) LIKE CONCAT('%', :searchString, '%') " +
            "OR CONCAT_WS(' ', c.name2, c.name) LIKE CONCAT('%', :searchString, '%')) " +
            "AND (:includeDeleted = true OR c.deletedDate is NULL)",
            countQuery = "SELECT COUNT(DISTINCT c) FROM Customer c INNER JOIN c.industry i WHERE " +
                    "c.role IN :roleFilter AND " +
                    "(:#{#industryFilter.size()} = 1 OR i.id IN :industryFilter) " +
                    "AND (:searchString IS NULL " +
                    "OR CONCAT_WS(' ', c.name, c.name2) LIKE CONCAT('%', :searchString, '%') " +
                    "OR CONCAT_WS(' ', c.name2, c.name) LIKE CONCAT('%', :searchString, '%')) " +
                    "AND (:includeDeleted = true OR c.deletedDate is NULL)")
    Page<Customer> findCustomers(@Param("roleFilter") Set<Role> roleFilter,
                                 @Param("industryFilter") Set<String> industryFilter,
                                 @Param("searchString") String searchString,
                                 @Param("includeDeleted") boolean includeDeleted, Pageable pageable);

请注意我们如何将输入传递给类似的人:CONCAT('%', :searchString, '%')

springBootVersion = '1.5.17.RELEASE'升级到springBootVersion = '2.1.3.RELEASE' (我们使用Gradle)之后,该查询将在运行时失败,只有一个例外:

org.hibernate.QueryException:未绑定的命名参数: includeDeleted

CONCAT('%', :searchString, '%')替换%:searchString%解决了这个问题。

我的问题是:为什么?

通过进入调试模式并遵循完整的调用堆栈,我可以看到从第205行的JdkDynamicAopProxy中观察到的方法调用中正确地检索了参数,从而生成了一个调用Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);,结果是:

代码语言:javascript
复制
argsToUse = {Object[5]@15562} 
 0 = {HashSet@15491}  size = 4
 1 = {HashSet@15628}  size = 1
 2 = null
 3 = {Boolean@15629} false
 4 = {PageRequest@15490} "Page request [number: 0, size 20, sort: name: ASC,name2: ASC]"

到目前一切尚好。然后,我们继续,调用的方法也被正确地解决了:

代码语言:javascript
复制
parameterTypes = {Class[5]@15802} 
 0 = {Class@198} "interface java.util.Set"
 1 = {Class@198} "interface java.util.Set"
 2 = {Class@311} "class java.lang.String"
 3 = {Class@15811} "boolean"
 4 = {Class@9875} "interface org.springframework.data.domain.Pageable"

然后,我们进一步到RepositoryFactorySupport第599行调用private Object doInvoke(MethodInvocation invocation) throws Throwable,它使用内部类public class QueryExecutorMethodInterceptor implements MethodInterceptor中的private final Map<Method, RepositoryQuery> queries; (我不确定这个变量是何时/如何创建和填充的),它包含存储库接口中使用@Query注释的所有查询。

对于我们的具体情况,它包含一个与我正在调用的查询(FindCustomers)匹配的条目(最后一个):

代码语言:javascript
复制
queries = {HashMap@16041}  size = 3
 0 = {HashMap$Node@16052} "public abstract com.swisscom.psp.domain.Customer com.swisscom.psp.repository.CustomerRepository.getOne(java.lang.String)" -> 
 1 = {HashMap$Node@16055} "public abstract boolean com.swisscom.psp.repository.CustomerRepository.existsWithRole(java.lang.String,java.util.Set)" -> 
 2 = {HashMap$Node@16058} "public abstract org.springframework.data.domain.Page com.swisscom.psp.repository.CustomerRepository.findCustomers(java.util.Set,java.util.Set,java.lang.String,boolean,org.springframework.data.domain.Pageable)" -> 

在扩展该条目时,我可以看到错误来自何处,:includeDeleted命名参数的绑定根本不存在:

代码语言:javascript
复制
value = {SimpleJpaQuery@16060} 
 query = {ExpressionBasedStringQuery@16069} 
  query = "SELECT DISTINCT c FROM Customer c INNER JOIN c.industry i WHERE c.role IN :roleFilter  AND (:__$synthetic$__1 = 1 OR i.id IN :industryFilter) AND (:searchString IS NULL OR CONCAT_WS(' ', c.name, c.name2) LIKE CONCAT('%', :searchString, '%') OR CONCAT_WS(' ', c.name2, c.name) LIKE CONCAT('%', :searchString, '%')) AND (:includeDeleted = true OR c.deletedDate is NULL)"
  bindings = {ArrayList@16089}  size = 6
   0 = {StringQuery$InParameterBinding@16092} "ParameterBinding [name: roleFilter, position: null, expression: null]"
   1 = {StringQuery$ParameterBinding@16093} "ParameterBinding [name: __$synthetic$__1, position: null, expression: #industryFilter.size()]"
   2 = {StringQuery$InParameterBinding@16094} "ParameterBinding [name: industryFilter, position: null, expression: null]"
   3 = {StringQuery$ParameterBinding@16095} "ParameterBinding [name: searchString, position: null, expression: null]"
   4 = {StringQuery$ParameterBinding@16096} "ParameterBinding [name: searchString, position: null, expression: null]"
   5 = {StringQuery$ParameterBinding@16097} "ParameterBinding [name: searchString, position: null, expression: null]"

现在,我有了前面提到的修复方法,但我仍然非常想知道以下几点,供将来参考:

  1. 何时以及如何创建和填充private final Map<Method, RepositoryQuery> queries变量?
  2. 究竟是什么导致了这个错误?我在升级过程中错过了什么吗?我是否使用/混合了不推荐的逻辑/错误的逻辑,并且应该进一步修改代码?

我们的数据库是MariaDB 10.1.36

编辑:在发生这种行为的所有地方(在某些地方仍然发生),未绑定参数总是最后一个参数。

EDIT2:其他人在升级后也有类似的行为,为什么会发生这种情况?参考文献

EDIT3:参考文献和这种奇怪的行为也有报道。有趣的是,如果我将已经连接的输入传递给:searchString (例如:%SOMETHING%),则不会得到异常,如果我离开%:searchString%,则会得到异常。是的,最后移动这些参数可以解决绑定方面的一些错误。

EDIT4:也许相关的错误

显然发生了一些奇怪的事情,所以:,这个绑定解析是如何发生的?

提前谢谢,祝您今天愉快

EN

回答 1

Stack Overflow用户

发布于 2019-04-03 12:42:26

实际上,据我所知,您的两种方法都不是使用通配符来处理LIKE的正确方法。相反,LIKE表达式应该是:

代码语言:javascript
复制
LIKE :searchString

您应该绑定到此参数:searchString

代码语言:javascript
复制
String searchString = "bananas";
String param = "%" + searchString + "%";
// then bind param to :searchString

也就是说,使用%通配符将整个字符串绑定在一起。然后,让数据库担心如何转义它。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55495377

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档