Поиск Spring CriteriaBuilder по названию

Когда я пытаюсь найти enum по его имени с Specification в моей БД с помощью Spring @Repository, я получаю следующее исключение:

Caused by: java.lang.IllegalArgumentException: Parameter value [HELLO] did not match expected type [application.springEnum.Hello (n/a)]

Но в БД перечисление сохранено как VARCHAR(255), так почему я могу искать перечисление с помощью String, почему это нужно по типу Enum?

DTO класс

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DTO {
    @Id
    private String id;
    @Enumerated(EnumType.STRING)
    private Hello helloEnum; // My Enum
}

Разъем базы данных

@Repository
public interface Connector extends JpaRepository<DTO, String>, JpaSpecificationExecutor<DTO> {
}

Стартер

@Component
public class Starter {
    @Autowired
    private Connector connector;

    @PostConstruct
    public void init(){
        // Create DTO entity
        DTO dto = DTO.builder()
                .id(UUID.randomUUID().toString())
                .helloEnum(Hello.HELLO)
                .build();
        // Save the entity in the db
        connector.save(dto);

        // Search by the name, here I get the excpetion
        List<DTO> result = connector.findAll((root, query, cb) ->
                cb.equal(root.get("helloEnum"), "HELLO")
        );
    }
}

Буду признателен за объяснение.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
6
0
4 873
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

public class Main {
enum Hello {
    HELLO
}
public static void main(String[] args) {
    Hello hello = Hello.HELLO;
    System.out.println(hello.toString().equals("HELLO")); //true
    System.out.println("HELLO".equals(hello.toString())); //true
    System.out.println(hello.toString() == "HELLO"); //true
    System.out.println(hello.equals("HELLO")); //false
    System.out.println("HELLO".equals(hello)); //false
//        System.out.println(hello == "HELLO"); //incompatible types
}
}

Не совсем уверен, но параметры equal () должны быть одного типа.

root.get("helloEnum")

это экземпляр Hello

"HELLO"

является экземпляром String.

Здесь нет автоматического преобразования toString () в enum. На всякий случай попробуйте:

// Search by the name, here I get the excpetion
    List<DTO> result = connector.findAll((root, query, cb) ->
            cb.equal(root.get("helloEnum").toString(), "HELLO")
    );

О том, как это представлено в базе данных, это ORM.

Peteef 07.05.2018 16:51

не компилируется событие

Toumi 24.09.2019 17:28
Ответ принят как подходящий

Вы пытаетесь сравнить Enum и String.

Попробуйте так:

List<DTO> result = connector.findAll((root, query, cb) ->
                cb.equal(root.get("helloEnum"), Hello.HELLO);

Я постараюсь дать некоторые объяснения, почему это происходит. Hibernate извлекает ResultSet из базы данных в подпись Class с помощью Reflection.

Наблюдая за stacktrace, вы увидите что-то вроде:

org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:90) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:55) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.AbstractProducedQuery.setParameter(AbstractProducedQuery.java:486) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.AbstractProducedQuery.setParameter(AbstractProducedQuery.java:104) ~[hibernate-core-5.2.16.Final.jar:5.2.16.Final]

Hibernate выполняет ряд проверок перед установкой параметра.

Вот последний метод, который инициализирует основную причину Exception:

public <P> void validate(Type paramType, Object bind, TemporalType temporalType) {
        if ( bind == null || paramType == null ) {
            // nothing we can check
            return;
        }
        final Class parameterType = paramType.getReturnedClass();
        if ( parameterType == null ) {
            // nothing we can check
            return;
        }

        if ( Collection.class.isInstance( bind ) && !Collection.class.isAssignableFrom( parameterType ) ) {
            // we have a collection passed in where we are expecting a non-collection.
            //      NOTE : this can happen in Hibernate's notion of "parameter list" binding
            //      NOTE2 : the case of a collection value and an expected collection (if that can even happen)
            //          will fall through to the main check.
            validateCollectionValuedParameterBinding( parameterType, (Collection) bind, temporalType );
        }
        else if ( bind.getClass().isArray() ) {
            validateArrayValuedParameterBinding( parameterType, bind, temporalType );
        }
        else {
            if ( !isValidBindValue( parameterType, bind, temporalType ) ) {
                throw new IllegalArgumentException(
                        String.format(
                                "Parameter value [%s] did not match expected type [%s (%s)]",
                                bind,
                                parameterType.getName(),
                                extractName( temporalType )
                        )
                );
            }
        }
    }

Метод private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType), который имеет кучу проверок, повторяет false, потому что ваш ожидаемый тип - class com.whatever.Hello, а значение для проверки - HELLO, то есть String, но типы Enum и String несовместимы!

Если вы укажете правильный Enum в критериях поиска, проверка будет пройдена, потому что private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType) содержит проверку isInstance, которая пройдет:

else if ( expectedType.isInstance( value ) ) {
    return true;
}

После всех проверок Hibernate извлекает значения из ResultSet и строит List, в данном конкретном случае элементы List выбираются с использованием отражения.

Я знаю, но в базе он сохраняется как varchar

Daniel Taub 07.05.2018 16:23

@Daniel Taub, пожалуйста, взгляните на обновленный ответ.

J-Alex 07.05.2018 22:47

Есть ли способ использовать метод построения критериев Like с перечислением?

abi_pat 13.01.2021 16:09

Другие вопросы по теме