【问题标题】:Criteria Query with filter of non String type - JPA/Hibernate Spring Boot具有非字符串类型过滤器的条件查询 - JPA/Hibernate Spring Boot
【发布时间】:2021-12-11 13:32:18
【问题描述】:

我正在尝试使用带有过滤器的条件查询从表 MyEntity 中获取行数。

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

例如。

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

如果过滤器是{"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/非字符串值的好方法?

【问题讨论】:

    标签: java spring hibernate criteria hibernate-criteria


    【解决方案1】:

    使用 JSONB 代替 JSON 或将 JSON 转换为 JSONB。您无法比较 JSON 值。您可以改为比较文本值。您可以使用 JSONB 类型解决此问题,该类型以分解的二进制格式存储。可以比较这种类型的值。

    SELECT 
    '["dramatic" ,"women", "political"]'::json::text =  
    '["dramatic","women","political"]'::json::text      -- yields false!
    
    
    SELECT 
    '["dramatic" ,"women", "political"]'::jsonb =  
    '["dramatic","women","political"]'::jsonb           -- yields true
    

    所以您的查询应该像这样工作以消除错误。

    SELECT * 
    FROM movie_test 
    WHERE tags::jsonb = '["dramatic","women","political"]'::jsonb
    

    【讨论】:

    • 嘿,谢谢。我会提出这个问题。这可以是一个备份选项。现在,我将不得不坚持使用 json。有没有办法查询 json 列?有没有办法通过 Criteria API 使用投射解决方案?
    【解决方案2】:

    @JoinColumn 注释指定用作目标实体的外键的列的名称。 第一个字段应该是 id 可能有这个问题,所以试试这个,或者它可能正在发生,因为这里的数据字段可以有一个空值,并且 empty 不是 Double 并且 empty 也不是 null。 Here is you can go through

    Here see for the data field

    【讨论】:

    • 谢谢,但我认为连接列关系映射没有任何问题。我已经编辑了这个问题并添加了一个例子来澄清一些事情。感谢堆栈溢出链接。他们使用 json_contains 函数来检查 json 中是否存在值,而我正在尝试获取 json 的完全匹配。
    【解决方案3】:

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

    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();
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-15
      • 2020-01-25
      • 2018-03-17
      • 1970-01-01
      • 1970-01-01
      • 2016-07-26
      • 2019-09-29
      相关资源
      最近更新 更多