Spring boot 2 @Query Разрешение значения привязки именованного параметра портится после обновления с 1.5 - PullRequest
0 голосов
/ 03 апреля 2019

У нас есть следующий рабочий запрос с использованием SpringBoot 1.5:

@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);

Обратите внимание, как мы передаем ввод в LIKE: CONCAT('%', :searchString, '%')

После обновления с springBootVersion = '1.5.17.RELEASE' до springBootVersion = '2.1.3.RELEASE' (мы используем Gradle) этот запрос не будет выполнен во время выполнения, за исключением:

org.hibernate.QueryException: Именованный параметр не связан: includeDeleted

Замена CONCAT('%', :searchString, '%') на %:searchString% устраняет проблему.

У меня такой вопрос: почему?

Войдя в режим отладки и следуя за полным стеком вызовов, я мог видеть, как правильно извлекаются параметры из вызова метода, как это отмечено в JdkDynamicAopProxy в строке 205, и Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); вызывает вызов, который приводит к:

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]"

Пока все хорошо. Затем мы продолжаем и метод для вызова также правильно разрешен:

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, которая использует private final Map<Method, RepositoryQuery> queries; из внутреннего класса public class QueryExecutorMethodInterceptor implements MethodInterceptor (я не уверен, когда / как была создана и заполнена эта переменная), которая содержит все запросы, помеченные @Query в моем интерфейсе репозитория.

Для нашего конкретного случая он содержит запись (последнюю), которая соответствует запросу, который я вызываю ( findCustomers ):

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 просто нет:

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: может быть, связано ошибка ?

Очевидно, что происходит что-то странное, поэтому: как именно это разрешение привязки происходит точно ?

Заранее спасибо и хорошего дня

1 Ответ

1 голос
/ 03 апреля 2019

На самом деле, насколько я знаю, ни один из ваших двух подходов не является правильным для использования здесь для обработки LIKE с подстановочным знаком.Вместо этого выражение LIKE должно быть:

LIKE :searchString

К этому параметру :searchString вы должны привязать:

String searchString = "bananas";
String param = "%" + searchString + "%";
// then bind param to :searchString

То есть вы связываете всю строку с помощью% шаблон, вместе.Затем дайте базе данных побеспокоиться о том, как ее избежать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...