Estoy usando la API Criterios para construir consultas con nombre utilizando filtros. Funciona en las comparaciones de cadenas normales, pero cuando se filtran en UUID que arroja el siguiente 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)]
Hay varias preguntas que abordan este tema, pero ninguno de ellos trabajaron, probé el siguiente:
- añadiendo
@Column(name = "id", updatable = false, nullable = false)
al campo entidad - añadiendo
@Type(type="org.hibernate.type.UUIDCharType")
al campo entidad - añadiendo
@Type(type="uuid-char")
al campo entidad
Foo entidad:
@Entity
//lombok stuff
public class Foo {
@Id
private UUID id;
private String name;
//...
}
SQL Variante:
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
es un método de ayuda simple que sólo utiliza criteriaBuilder.and (predicado, newPredicate)
FooFilter
sólo tiene campos de Cuerda
He arreglado el problema utilizando encasillamiento que proporciona la API Criteria.
Antes de:
addAndPredicate(..., ..., cb.like(cb.upper(foo.get("id")), ...));
Después:
addAndPredicate(..., ..., cb.like(cb.upper(foo.get("id").as(String.class)), ...));
De: 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)
Realizar un encasillado de la expresión, que devuelve un nuevo objeto de expresión. Este método no causa la conversión de tipos: el tipo de ejecución no se cambia. Advertencia: puede resultar en un fallo de tiempo de ejecución.