Когда я пытаюсь найти 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")
);
}
}
Буду признателен за объяснение.




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")
);
не компилируется событие
Вы пытаетесь сравнить 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, пожалуйста, взгляните на обновленный ответ.
Есть ли способ использовать метод построения критериев Like с перечислением?
О том, как это представлено в базе данных, это ORM.