J'utilise l'API Criteria pour créer des requêtes nommées à l'aide de filtres. Cela fonctionne sur les comparaisons de chaînes normales, mais lors du filtrage sur UUID, il génère l'erreur suivante:
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)]
Il y a plusieurs questions abordant ce problème mais aucune d'elles n'a fonctionné, j'ai essayé ce qui suit:
@Column(name = "id", updatable = false, nullable = false)
au champ d'entité@Type(type="org.hibernate.type.UUIDCharType")
au champ d'entité@Type(type="uuid-char")
au champ d'entitéentité Foo:
@Entity
//lombok stuff
public class Foo {
@Id
private UUID id;
private String name;
//...
}
Variante SQL:
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
est une méthode d'assistance simple qui utilise simplement critèreBuilder.and (predicate, newPredicate)
FooFilter
n'a que des champs String
J'ai résolu le problème en utilisant le typage fourni par l'API Criteria.
Avant:
addAndPredicate(..., ..., cb.like(cb.upper(foo.get("id")), ...));
Aprè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)
Effectuez un transtypage sur l'expression, en renvoyant un nouvel objet d'expression. Cette méthode n'entraîne pas de conversion de type: le type d'exécution n'est pas modifié. Attention: peut entraîner un échec d'exécution.