Спецификация JPA и нулевой параметр в предложении .where

Я написал две спецификации, которые возвращают null, если их параметр равен null.

public static Specification<Prodotto> getProdottoByLineaSpec (String linea) {

        if (linea != null) {
            return (root, query, criteriaBuilder) -> {
                return criteriaBuilder.like((root.join("linea")).get("nome"), "%"+linea+"%");
            };
        }
        else return null;
    }

public static Specification<Prodotto> getProdottoByIngSpec (String ing) {

        if (ing != null) {
            return (root, query, criteriaBuilder) -> {
                return criteriaBuilder.like(((root.join("listaQuoteIng")).join("ing")).get("nome"), "%"+ing+"%");
            };
        }
        else return null;
    }

Затем я создал третий, который объединяет предыдущие с оператором and внутри предложения where:

public static Specification<Prodotto> getProdottoByMainTraits (String linea, String ing) {

        return Specification.where(getProdottoByLineaSpec(linea).and(getProdottoByIngSpec(ing)));
    }

А теперь самое смешное:

  • Если ByLinea возвращает null, я получаю nullPointerException из checkPackageAccess при разрешении предложения where.
  • Если ByIng возвращает null, он просто игнорируется (как и должно быть), и запрос соответствует только другому предикату.
  • Если я поменяю местами два предиката, поставив ByIng первым, а затем ByLinea внутри предложения where, все будет работать в любой комбинации.

person 4javier    schedule 31.01.2020    source источник


Ответы (1)


Рекомендуется избегать возврата null из методов.

Вы можете использовать criteriaBuilder.conjunction() игнорировать null параметр Specification. Он генерирует всегда true Predicate. Есть противоположный метод criteriaBuilder.дизъюнкция ()

public static Specification<Prodotto> getProdottoByLineaSpec (String linea) {
                        
    return (root, query, criteriaBuilder) -> {
        if (linea == null) {                 
            return criteriaBuilder.conjunction();
        }

        return criteriaBuilder.like((root.join("linea")).get("nome"), "%" + linea + "%");            
    }
}

P.S. Вы получите NullPointerException, если сначала Specification будет null попытка доступа к методу and. Чтобы было понятно, это выглядит так

Specification.where(null.and(getProdottoByIngSpec(ing)));

Но если только второй Specification будет null этот работает

Specification.where(getProdottoByLineaSpec(linea).and(null));

потому что и параметр метода может быть null

person alex valuiskyi    schedule 31.01.2020
comment
Спасибо за ваше предложение. Я не знал этих методов. Это действительно работает, вам просто нужно вызвать лямбда-функцию внутри блока else else return (root, query, criteriaBuilder) -> criteriaBuilder.conjunction(); Я поддерживаю ваш ответ, но подождите, чтобы пометить его как решение, потому что это не объясняет, почему моя исходная структура не вызвала проблем, если я изменю порядок предикатов внутри предложения where . - person 4javier; 01.02.2020
comment
Это моя вина в блоке else. Я написал код с помощью телефона. Я отредактировал ответ. - person alex valuiskyi; 01.02.2020
comment
Ответ также содержит NullPointerException объяснение причины - person alex valuiskyi; 01.02.2020
comment
Вчера я столкнулся со странным поведением: если я установил ByIng в качестве первого предиката и заставил его возвращать значение null, у меня не было проблемы! Это было то, чего я не мог понять. Теперь я только что повторил попытку, и на самом деле он выдает ожидаемое исключение. Наверное, вчера устал, и сделал какую-то ошибку. Как правило, всегда ли лучше, чтобы Спецификация возвращала соединение/дизъюнкт вместо нулевого значения, когда вы хотите, чтобы оно допускало значение NULL? - person 4javier; 01.02.2020
comment
Да. Или вы должны обрабатывать все возможные нули. Но этот подход более подвержен ошибкам - person alex valuiskyi; 01.02.2020
comment
Имейте в виду, что каждая конъюнкция и дизъюнкция добавляют дополнительное предложение where в оператор sql. Для конъюнкции это выглядит так: 1=1. - person alex valuiskyi; 01.02.2020
comment
Не думал об этом ... возможно, этот другой подход увеличивает нагрузку на службу, но снижает нагрузку на БД. stackoverflow.com/a/48210438/6350583 Что вы думаете? - person 4javier; 01.02.2020
comment
Я могу выбрать этот подход только для частных методов. Но для внешнего API я стараюсь не возвращать значение null. Предложения конъюнкции и дизъюнкции не вызывают никакой рабочей нагрузки. Они используются в качестве маркеров предикатов. - person alex valuiskyi; 01.02.2020
comment
Разве вы не писали, что союз добавляет к запросу дополнительное предложение where? - person 4javier; 01.02.2020
comment
Да, но такие пункты, как 1=1, ничего не стоят - person alex valuiskyi; 01.02.2020
comment
Идеальный. Я отметил ваш ответ как решение. Большое спасибо. - person 4javier; 01.02.2020