InvalidDataAccessApiUsageException:参数值 [...] 与预期类型不匹配 [java.util.UUID (n/a)]

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

我正在使用 Criteria API 通过过滤器构建命名查询。它适用于正常的字符串比较,但在对 UUID 进行过滤时会抛出以下错误:

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)]

有几个问题解决了这个问题,但 none 个有效,我尝试了以下方法:

Foo 实体:

@Entity
//lombok stuff
public class Foo {

    @Id
    private UUID id;
    private String name;
    //...
}

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);
    //...
}

Foo规格:

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 是一个简单的辅助方法,只使用 criteriaBuilder.and(predicate,newPredicate)

FooFilter 只有字符串字段

我使用标准 API 提供的类型转换解决了这个问题。

之前:

addAndPredicate(..., ..., cb.like(cb.upper(foo.get("id")), ...));

之后:

addAndPredicate(..., ..., cb.like(cb.upper(foo.get("id").as(String.class)), ...));

发件人: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.

问题是您的 FooFilter 仅包含 String 字段,而您正试图将 String idcreateIdPredicate() 中的 UUID 对象进行比较。这就是抛出异常的原因。有2个解决方案:

  • 要么,您应该按照@tentacle
  • 的建议将createIdPredicate()中的filter.getId().toUpperCase()部分替换为UUID.fromString(filter.getId())
  • 或者,更改您的 FooFilter 过滤器,使 id 的类型为 java.util.UUID

顺便说一下,恕我直言,将 UUIDslike 进行比较并不是一个好主意,因为一个 UUID 永远不会像另一个 UUID,每个 UUID 对象是唯一的。您的谓词应检查由 FooFilter 传输的 ID 与来自 DB.

的 ID 是否相等

我刚刚遇到了类似的问题,@edward 的回答对我帮助很大 :)

我更改的一件事是使用 cb.equal 而不是 cb.like,因为在搜索特​​定 ID 时 equal 方法似乎更有效。

我做的另一件事是只用 UUID.fromString(uuid_string_param) 解析一次我的过滤器参数,这样我只解析一次参数,而不必将数据库 UUID 字段转换为字符串以获得匹配.

干杯。