使用带有 Spring Data 和绑定参数的 Postgres JSONB 查询失败并出现 InvalidDataAccessApiUsageException

Posted

技术标签:

【中文标题】使用带有 Spring Data 和绑定参数的 Postgres JSONB 查询失败并出现 InvalidDataAccessApiUsageException【英文标题】:Using Postgres JSONB query with Spring Data and bind parameter fails with InvalidDataAccessApiUsageException 【发布时间】:2018-05-28 03:49:08 【问题描述】:

我目前正在寻找异常的解决方案

org.springframework.dao.InvalidDataAccessApiUsageException: Parameter with that position [1] did not exist;

我目前的@Query注解是:

@Query(
    nativeQuery = true, 
    value = "SELECT * FROM thgcop_order_placement WHERE \"order_info\" @> '\"parentOrderNumber\":\" :param \"'")

我猜position [1] did not exist 来自于它的双引号加双引号加单引号。

我怎样才能做到这一点?

查询使用 Postgres JSONB 数据类型。列定义为ORDER_INFO JSONB

以下本机查询在 Postgres 客户端中运行良好:

SELECT * FROM thgcop_order_placement
WHERE "order_info" @> '"parentOrderNumber":"ORD123"'

【问题讨论】:

Postgres 是否支持这种方式的绑定变量?我认为:param 周围的引号是多余的。 你能包含完整的堆栈跟踪吗? @JensSchauder,如果我们考虑一下我在“更新”块中提到的纯 SQL 查询,那么在 1 点。这是完全正确的。如果我尝试删除引号,那么 Postgres 会出错。这就是为什么我坚信它应该是必需的。 【参考方案1】:

除了以下之外,以上都不适合我,

服务层代码:-

OrderInfo orderInfo = new OrderInfo();
orderInfo.setParentOrderNumber("ORD123");
....
String param = objectMapper.writeValueAsString(orderInfo);
List<Order> list = jpaRepository.getByParentOrderNumber(param);

JpaRepository.java 代码:-

@Query(nativeQuery = true, value = "select * from thgcop_order_placement where order_info @> CAST(:condition as jsonb)")
List<Order> getByParentOrderNumber(@Param("condition") String parentOrderNumber);

这就是我实现结果的方式。我希望这对所有热心的人都非常有帮助!!

谢谢大家的帮助!!!

【讨论】:

【参考方案2】:

TL;DR:首先使其与绑定参数和普通 JDBC 一起使用。然后继续使用 Spring Data,可能会退回到自定义实现。

您在这里面临许多层面的问题。

    让我们暂时先忽略 Spring Data。 您显示的语句与您尝试使用 Spring Data 构建的语句非常不同,因为它不包含绑定变量。 所以不是

    SELECT * FROM thgcop_order_placement WHERE "order_info" @> '"parentOrderNumber":"ORD123"'
    

    我们应该比较一下

    SELECT * FROM thgcop_order_placement WHERE "order_info" @> '"parentOrderNumber": ? '
    

    请注意,我们丢失了引号,因为它们表示文字字符串,但我们提供的不是文字字符串而是绑定参数。

    我没有发现任何迹象表明您可以在部分 JSON 表达式中使用绑定参数。因此,我们需要使用以下语句而不是上面的语句:

    SELECT * FROM thgcop_order_placement WHERE "order_info" @> ?
    

    当然,绑定参数应该包含完整的 JSON 表达式

    不幸的是,这似乎也不起作用,因为现在 Postgres 将绑定参数视为 VARCHAR 而不是 JSON 表达式。见https://blog.2ndquadrant.com/processing-json/。我认为正确的版本应该是

    SELECT * FROM thgcop_order_placement WHERE "order_info" @> ?::json
    

    但我也无法让它工作。

    无论如何,您都需要将参数转换为 JSON 结构。 通常我会建议为此使用 SpEL 表达式。但它不起作用,因为 Spring Data 阻塞了 SpEL 表达式中所需的花括号,并认为它们是 SpEL 表达式的结尾。

    如果您使用简单的 JDBC 连接或 JdbcTemplate 来处理类似的事情,您可以开始考虑使用 @Query 注释。

    @Query(
        value= "SELECT * FROM thgcop_order_placement WHERE \"order_info @> :name::json",
        nativeQuery = true)
    

这可能会引发更多问题,因为 Spring Data 会考虑 ::json 参数名称的一部分。如果是这种情况,您将不得不依靠自定义实现。

I ran a couple of experiments, which you can look at and play with here.

【讨论】:

我会试一试并告诉你。【参考方案3】:

尝试如下绑定参数

@Query(nativeQuery = true, value = "SELECT * FROM thgcop_order_placement"
  + " WHERE \"order_info\" @> '\"parentOrderNumber\":\" ?1 \"'")

【讨论】:

【参考方案4】:

我也被同样的问题困住了一段时间。在解析以字符串格式存在的查询时,springboot 似乎搞砸了。然而,这是我找到的解决方案,它将直接作为存储库中的本机查询工作:

由于order_info 属于jsonb 类型,因此可以将正在搜索的值转换为jsonb 值。 要搜索的值:"parentOrderNumber":"ORD123" 让我们转义整个字符串以供 java 解析。 String searchString = "\"parentOrderNumber\":\"ORD123\"" 现在,让我们以 Spring 能够理解的方式输入 postgres 查询。
@Query(
value = "SELECT * from thgcop_order_placement where ((?1\\:\\:jsonb) <@ (order_info\\:\\:jsonb))",
nativeQuery=true
)
List<Order> getByParentOrderNumber(String searchString);

在哪里,

Spring 会将?1 替换为searchString 的值,如上所定义。 :: 是类型转换运算符,我们使用它显式地将传递的参数 (searchString) 类型转换为 jsonb。因此,在尝试搜索之前,值 \"parentOrderNumber\":\"ORD123\" 会转换为 jsonb。 此外,order_info 列的值也显式转换为 jsonb。 现在,当两个项目(要搜索的值和列)是相同的数据类型时,我们可以使用&lt;@ 运算符来检查搜索字符串是否包含在列值中。

在服务层面,我们只需要这样做:

String orderNumber = "-- some order value e.g. ORD123 --"
String searchString = "\"parentOrderNumber\":\"" + orderNumber + "\""
List<Order> list = jpaRepository.getByParentOrderNumber(searchString);

关于 Postgres JSON 操作符的更多细节可以在官方文档中找到:https://www.postgresql.org/docs/9.5/functions-json.html

【讨论】:

以上是关于使用带有 Spring Data 和绑定参数的 Postgres JSONB 查询失败并出现 InvalidDataAccessApiUsageException的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC Data Binding

在 spring data jpa 存储库中搜索许多可选参数

datetimepicker 值未绑定到带有 thymeleaf 的属性 spring

spring data mongodb Query 及分页

Spring Data JPA - NUMBER 列类型的空参数问题

带有分页的 Spring Data 和 Native Query