首页 > 解决方案 > 具有非字符串类型过滤器的条件查询 - JPA/Hibernate Spring Boot

问题描述

我正在尝试使用带有过滤器的 Criteria Query 从表 MyEntity 中获取行数。

此过滤器(函数参数)是一个 JsonNode,其中键(JsonNode 过滤器)作为实体中的列,值(JsonNode 过滤器)可以是 String 或 JsonNode 类型,具体取决于用户传递的过滤器。

例如。

如果过滤器是{"policy": {"id": "123"}},则搜索policy具有 json 类型值的列{"id": "123"}

如果 filter 是{"string_column": "test_string"},则搜索string_column具有字符串类型值的列test_string

使用的数据库是 PostgreSQL。

这是我的代码 -

    public Long getCountForMyEntity(JsonNode filter) {
        CriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder();
        CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);
        Root<MyEntity> root = query.from(MyEntity.class);
        query.select(criteriaBuilder.count(root));

        Iterator<Map.Entry<String, JsonNode>> filterFields = filter.fields();

        while(filterFields.hasNext()) {
            Map.Entry<String, JsonNode> field = filterFields.next();
            String   fieldName  = field.getKey();
            JsonNode fieldValue = field.getValue();
            Object value = fieldValue;

            System.out.println(fieldValue + " : " + fieldValue.getNodeType());
            if (fieldValue.getNodeType() == JsonNodeType.STRING) {
                value = fieldValue.asText();
            }

            query.where(criteriaBuilder.equal(root.get(fieldName), value));
        }

        return sessionFactory.getCurrentSession().createQuery(query).getSingleResult();
    }

当过滤器值为字符串但过滤器值为 json 时,这适用于获取计数,我收到以下错误 -

44- [WARN ] 2021-10-26 14:42:35 [http-nio-9090-exec-7] o.h.e.j.s.SqlExceptionHelper [][][][] - SQL Error: 0, SQLState: 42883
45- [ERROR] 2021-10-26 14:42:35 [http-nio-9090-exec-7] o.h.e.j.s.SqlExceptionHelper [][][][] - ERROR: operator does not exist: json = unknown
  Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
  Position: 121
46- [WARN ] 2021-10-26 14:42:35 [http-nio-9090-exec-7] o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver [][][][] - Resolved [org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet]

我试过像这样直接传递fieldValue,但仍然是同样的错误-

query.where(criteriaBuilder.equal(root.get(fieldName), fieldValue));

知道我哪里出错了吗?有没有过滤json/非字符串值的好方法?

标签: javaspringhibernatecriteriahibernate-criteria

解决方案


从 Narasimha 的铸造技术中获得灵感,但铸造为 String 而不是 jsonb。在与我们传递的转换为字符串的 JsonNode 进行比较之前,它将存储在 db 中的 json 转换为 String。

query.where(criteriaBuilder.equal(root.get(fieldName).as(String.class), fieldValue.toString()));

完整代码如下所示 -

    public Long getCountForMyEntity(JsonNode filter) {
        CriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder();
        CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);
        Root<MyEntity> root = query.from(MyEntity.class);
        query.select(criteriaBuilder.count(root));

        Iterator<Map.Entry<String, JsonNode>> filterFields = filter.fields();

        while(filterFields.hasNext()) {
            Map.Entry<String, JsonNode> field = filterFields.next();
            String   fieldName  = field.getKey();
            JsonNode fieldValue = field.getValue();

            System.out.println(fieldValue + " : " + fieldValue.getNodeType());
            if (fieldValue.getNodeType() == JsonNodeType.STRING) {
                query.where(criteriaBuilder.equal(root.get(fieldName), fieldValue.asText()));
            } else {
                query.where(criteriaBuilder.equal(root.get(fieldName).as(String.class), fieldValue.toString()));
            }
        }

        return sessionFactory.getCurrentSession().createQuery(query).getSingleResult();
    }

推荐阅读