I'm using Criteria API to build named queries using filters. It works on normal String comparisons but when filtering on UUID it throws the following error:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [67279329-5096-4196-9E73-748B33122CE2] did not match expected type [java.util.UUID (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [67279329-5096-4196-9E73-748B33122CE2] did not match expected type [java.util.UUID (n/a)]
There are several questions addressing this issue but none of them worked, I tried the following:
@Column(name = "id", updatable = false, nullable = false)
to the entity field@Type(type="org.hibernate.type.UUIDCharType")
to the entity field@Type(type="uuid-char")
to the entity fieldFoo entity:
@Entity
//lombok stuff
public class Foo {
@Id
private UUID id;
private String name;
//...
}
SQL Variant:
CREATE TABLE foo
(
id UUID NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL,
...
);
FooController:
@GetMapping(value = "/foo")
public ResponseEntity<List<Foo>> findFoos(@RequestParam Map<String, String> filterArguments) {
FooFilter filter = filterMapper.map(filterArguments);
FooSpecification spec = new FooSpecification(filter);
List<Foo> foos = fooRepo.findAll(spec);
//...
}
FooSpecification:
public class FooSpecification extends SpecificationHelper implements Specification<Foo> {
private final FooFilter filter;
public FooSpecification(FooFilter filter) {
this.filter = filter;
}
@Override
public Predicate toPredicate(Root<Foo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Predicate predicate = null;
predicate = createIdPredicate(root, filter, criteriaBuilder, predicate);
predicate = createNamePredicate(root, filter, criteriaBuilder, predicate);
// ...
return predicate;
}
private Predicate createIdPredicate(Root<Foo> foo, FooFilter filter, CriteriaBuilder cb, Predicate predicate) {
Predicate returnPredicate = predicate;
if (StringUtils.isNotBlank(filter.getId()))
returnPredicate = addAndPredicate(cb, predicate, cb.like(cb.upper(foo.get("id")), "%" + filter.getId().toUpperCase() + "%"));
return returnPredicate;
}
private Predicate createNamePredicate(Root<Foo> foo, FooFilter filter, CriteriaBuilder cb, Predicate predicate) {
Predicate returnPredicate = predicate;
if (StringUtils.isNotBlank(filter.getName()))
returnPredicate = addAndPredicate(cb, predicate, cb.like(cb.upper(foo.get("name")), "%" + filter.getName().toUpperCase() + "%"));
return returnPredicate;
}
}
addAndPredicate
is a simple helper method that just uses criteriaBuilder.and(predicate,newPredicate)
FooFilter
only has String fields
The problem is that your FooFilter
contains only String
fields, and you are trying to compare String id
with a UUID
object in createIdPredicate()
. And that's why the exception is thrown. There are 2 solutions:
filter.getId().toUpperCase()
part in createIdPredicate()
with UUID.fromString(filter.getId())
as suggested by @tentacleFooFilter
filter so that the id
would be of type java.util.UUID
.By the way, IMHO, comparing UUIDs
with like
is not a good idea, because one UUID
can never be like another UUID
, each UUID
object is unique. Your predicate should check the equality of ids transferred by FooFilter
and the one from DB
.
I fixed the issue using typecasting provided by the Criteria API.
Before:
addAndPredicate(..., ..., cb.like(cb.upper(foo.get("id")), ...));
After:
addAndPredicate(..., ..., cb.like(cb.upper(foo.get("id").as(String.class)), ...));
From: https://docs.oracle.com/javaee/6/api/javax/persistence/criteria/Expression.html#as(java.lang.Class)
<X> Expression<X> as(java.lang.Class<X> type)
Perform a typecast upon the expression, returning a new expression object. This method does not cause type conversion: the runtime type is not changed. Warning: may result in a runtime failure.
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