使用带有 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
。
现在,当两个项目(要搜索的值和列)是相同的数据类型时,我们可以使用<@
运算符来检查搜索字符串是否包含在列值中。
在服务层面,我们只需要这样做:
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 data jpa 存储库中搜索许多可选参数
datetimepicker 值未绑定到带有 thymeleaf 的属性 spring