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