When I'm trying to search enum by his name with Specification
in my DB using Spring @Repository
, I'm getting the following exception:
Caused by: java.lang.IllegalArgumentException: Parameter value [HELLO] did not match expected type [application.springEnum.Hello (n/a)]
But in the DB the enum saved as VARCHAR(255)
so why I can search the enum with String
, why It's need to by a Enum type?
DTO class
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DTO {
@Id
private String id;
@Enumerated(EnumType.STRING)
private Hello helloEnum; // My Enum
}
DataBase connector
@Repository
public interface Connector extends JpaRepository<DTO, String>, JpaSpecificationExecutor<DTO> {
}
Starter
@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")
);
}
}
I would appreciate for an explanation.
You're trying to compare Enum
and String
.
Try this way:
List<DTO> result = connector.findAll((root, query, cb) ->
cb.equal(root.get("helloEnum"), Hello.HELLO);
I will try to provide some explanations why this is happening. Hibernate fetches ResultSet
from Database to Class
signature using Reflection
.
Observing stacktrace you will see something like:
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 performs a bunch of validations before setting a parameter.
Here is the last method which initializes a root cause for 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 )
)
);
}
}
}
The method private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType)
which has a bunch of checks retuns false
because your expected type is class com.whatever.Hello
and value to check is HELLO
what is String
, but Enum
type and String
are incompatible!
If you will put proper Enum
in your search criteria, validation will pass because private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType)
contains isInstance
check which will pass:
else if ( expectedType.isInstance( value ) ) {
return true;
}
After all the checks Hibernate extracts values from ResultSet
and builds the List
, in this particular case, elements of List
are fetched using reflection.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With