Spring CriteriaBuilder поиск по его имени - PullRequest
0 голосов
/ 07 мая 2018

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

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

Но в БД enum сохранен как VARCHAR(255), так почему я могу искать в enum с 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")
        );
    }
}

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

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

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

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

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

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

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

org.hibernate.query.spi.QueryParameterBindingValidator.validate (QueryParameterBindingValidator.java:54) ~ [hibernate-core-5.2.16.Final.jar: 5.2.16.Final] в org.hibernate.query.spi.QueryParameterBindingValidator.validate (QueryParameterBindingValidator.java:27) ~ [hibernate-core-5.2.16.Final.jar: 5.2.16.Final] в org.hibernate.query.internal.QueryParameterBindingImpl.validate (QueryParameterBindingImpl.java:90) ~ [hibernate-core-5.2.16.Final.jar: 5.2.16.Final] в org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue (QueryParameterBindingImpl.java:55) ~ [hibernate-core-5.2.16.Final.jar: 5.2.16.Final] в org.hibernate.query.internal.AbstractProducedQuery.setParameter (AbstractProducedQuery.java:486) ~ [hibernate-core-5.2.16.Final.jar: 5.2.16.Final] в org.hibernate.query.internal.AbstractProducedQuery.setParameter (AbstractProducedQuery.java:104) ~ [Зимуют-ядро-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 type и String являются несовместимы!

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

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

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

0 голосов
/ 07 мая 2018
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
}
}

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

root.get("helloEnum")

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

"HELLO"

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

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

// Search by the name, here I get the excpetion
    List<DTO> result = connector.findAll((root, query, cb) ->
            cb.equal(root.get("helloEnum").toString(), "HELLO")
    );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...