Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InvalidDataAccessApiUsageException: Parameter value [...] did not match expected type [java.util.UUID (n/a)]

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:

  • adding @Column(name = "id", updatable = false, nullable = false) to the entity field
  • adding @Type(type="org.hibernate.type.UUIDCharType") to the entity field
  • adding @Type(type="uuid-char") to the entity field

Foo 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

like image 892
Edward Avatar asked Mar 10 '20 12:03

Edward


2 Answers

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:

  • Either, you should replace filter.getId().toUpperCase() part in createIdPredicate() with UUID.fromString(filter.getId()) as suggested by @tentacle
  • Or, change your FooFilter 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.

like image 190
Armine Avatar answered Nov 19 '22 03:11

Armine


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.

like image 1
Edward Avatar answered Nov 19 '22 02:11

Edward