如何在 jOOQ 中转换“to_json()”PostgreSQL 函数?

Posted

技术标签:

【中文标题】如何在 jOOQ 中转换“to_json()”PostgreSQL 函数?【英文标题】:How to convert "to_json()" PostgreSQL function in jOOQ? 【发布时间】:2020-09-25 02:26:09 【问题描述】:

我正在尝试将以下 PostgreSQL 查询转换为 jOOQ,但似乎无法找到解决此问题的适当方法。这是查询:

SELECT b.book_id AS b_id, b.title, b.price, b.amount, b.is_deleted,
to_json(array_agg(DISTINCT aut.*)) as authors, 
to_json(array_agg(DISTINCT cat.*)) as categories 
FROM book b 
LEFT JOIN author_book AS ab ON b.book_id = ab.book_id
LEFT JOIN author AS aut ON ab.author_id = aut.author_id
LEFT JOIN category_book AS cb ON b.book_id = cb.book_id
LEFT JOIN category AS cat ON cb.category_id = cat.category_id
GROUP BY b_id ORDER BY b_id ASC;

到目前为止,这是我在 jOOQ 中想到的:

    dslContext
   .select(BOOK.BOOK_ID, BOOK.TITLE, BOOK.PRICE, BOOK.AMOUNT, BOOK.IS_DELETED, 
           DSL.val(DSL.jsonArray(DSL.arrayAggDistinct(AUTHOR.AUTHOR_ID), 
                                 DSL.arrayAggDistinct(AUTHOR.FIRST_NAME),
                                DSL.arrayAggDistinct(AUTHOR.LAST_NAME))
               ).cast(SQLDataType.JSON),                    
        DSL.val(DSL.jsonArray(DSL.arrayAggDistinct(CATEGORY.CATEGORY_ID), 
                              DSL.arrayAggDistinct(CATEGORY.NAME), 
                              DSL.arrayAggDistinct(CATEGORY.IS_DELETED))
            ).cast(SQLDataType.JSON)
    ).from(BOOK
            .leftJoin(AUTHOR_BOOK).on(BOOK.BOOK_ID.eq(AUTHOR_BOOK.BOOK_ID))
            .leftJoin(AUTHOR).on(AUTHOR_BOOK.AUTHOR_ID.eq(AUTHOR.AUTHOR_ID))
            .leftJoin(CATEGORY_BOOK).on(BOOK.BOOK_ID.eq(CATEGORY_BOOK.BOOK_ID))
            .leftJoin(CATEGORY).on(CATEGORY_BOOK.CATEGORY_ID.eq(CATEGORY.CATEGORY_ID))
    ).where(AUTHOR.AUTHOR_ID.eq(Long.valueOf(authorId))
    ).groupBy(BOOK.BOOK_ID).orderBy(BOOK.BOOK_ID.asc())

当我执行 jOOQ 代码时,出现以下异常:

方言 DEFAULT 不支持类型类 org.jooq.impl.JSONArray

我用谷歌搜索了这个异常,在this 问题中只发现了这个类似的异常。是否有任何适当的解决方法来解决 PostgreSQL to_json 函数的转换以及在给定 aut(hor)/cat(egory) 表的所有字段上保存 DISTINCT 的 array_agg() 组合?

更新: 这是我用来在 jOOQ 中以非类型安全的方式编写查询的另一种方式:

dslContext.resultQuery(DBQueries.GET_ALL_BOOKS_BY_AUTHOR_ID, Long.valueOf(authorId)));

...这里是DBQueries.GET_ALL_BOOKS_BY_AUTHOR_ID 变量的代码:

public class DBQueries 
//...
static String GET_ALL_BOOKS_BY_AUTHOR_ID = "SELECT b.book_id AS b_id, b.title, b.price, b.amount, b.is_deleted, " +
            "to_json(array_agg(DISTINCT aut.*)) as authors, to_json(array_agg(DISTINCT cat.*)) as categories " +            
            "FROM book b " + 
            "LEFT JOIN author_book AS ab ON b.book_id = ab.book_id " +
            "LEFT JOIN author AS aut ON ab.author_id = aut.author_id " +
            "LEFT JOIN category_book AS cb ON b.book_id = cb.book_id " +
            "LEFT JOIN category AS cat ON cb.category_id = cat.category_id " +
            "WHERE aut.author_id = :id " + 
            "GROUP BY b_id ORDER BY b_id ASC;"; 

顺便说一句,我已经更新了我的问题,以便清楚我如何使用 resultQuery() 方法来实现我想要的。我在问如何在 jOOQ 中以尽可能类型安全的方式实现这一点,因为我有更复杂的查询,我不知道是否可以通过不使用 PostgreSQL to_json() 函数来实现。例如,我想在 jOOQ 中创建类似 this 的内容,例如 bytefish.de 网站提交链接的“04_create_functions.sql”部分。

get_image
--------------------------------------------------------------
 "imageid" : 1, 
  "hash" : "a3b0c44", 
  "description" : "Some Description", 
  "created_on" : "2015-03-09T22:00:45.111", 
  "comments" : ["commentid":1, "imageid":1, "text":"Awesome!", "createdon":"2015-03-09T22:58:47.783",
                "commentid":2, "imageid":1, "text":"This is just a second comment.", "createdon" : "2015-03-09T22:58:47.783"], 
  "tags" : ["tagid":1,"name":"Cool", 
            "tagid":2,"name":"Berlin"]
(1 row)

我正在尝试为(书)订单的报告做同样的事情,其中​​查询我涉及 8 个表(我没有在此处发布该查询,因为我试图解决 jOOQ 中更简单的问题,即创建 JsonArray)。

【问题讨论】:

【参考方案1】:

您的具体错误

您将Field 表达式(DSL.jsonArray()) 包装在DSL.val() 中,这意味着绑定值。这不起作用,是您异常的原因 但是,您还不能通过以这种方式将普通数组包装成JSON 数组。 jOOQ 不知道您的意思是在幕后调用to_json。从 jOOQ 3.13 开始,您将不得不求助于使用一些 plain SQL templating(参见下面的示例),或者,您获取一个数组而不在 SQL 逻辑中将其转换为 JSON,并且仅在您的 Java 客户端逻辑中将其转换为 JSON .

普通 SQL 模板示例:

DSL.field("to_json(0)", JSON.class, DSL.arrayAggDistinct(AUTHOR.AUTHOR_ID))

jOOQ 3.14 JSON 支持

请注意,jOOQ 3.14 将通过标准 SQL JSON API 和 SQL Server 的 FOR JSON 子句支持更多更多 JSON 功能:

JSON_ARRAY, JSON_OBJECT, JSON_VALUE function support JSON_ARRAYAGG support JSON_OBJECTAGG support JSON_TABLE table valued function support JSON_EXISTS predicate support

还有:

SQL Server FOR JSON syntax support and emulation

【讨论】:

如果我理解正确,您建议使用“普通 SQL 模板”来构建最终结果,方法是使用 命名/内联参数 并将 AUTHORS 和 CATEGORIES 作为普通参数值的数组 ORR 使用类似resultQuery​(String plainSQL, Object.. bindings) 的方法,在这种方法中,我将一次性传递普通的书面 SQL 查询和(在这种情况下)authorId 的值(我正在执行搜索)(但这不是类型安全的方式) ? 我已经更新了我的问题,以便更清楚地了解我想要实现的目标。 @NikolaS:不,不是针对整个查询,只是针对那个函数调用!我会更新我的答案 @Lukas_Eder:我听取了您的建议,查看了文档并按照您和 Docs 的示例调整了我的 jOOQ 代码。解决方案可以在我自己的答案中看到,如果您有任何建议/cmets,请告诉我。再次感谢。 :) @NikolaS:完美,感谢您在此处记录您自己的解决方案,作为答案。人们应该更频繁地这样做!【参考方案2】:

正如@Lukas_Eder 指出的那样,要解决这个问题,需要通过普通 SQL 模板创建 JSON 对象,如下例所示:

DSL.field("to_json(0)", JSON.class, DSL.arrayAggDistinct(AUTHOR.AUTHOR_ID))

我在文档中阅读了有关上述方法的更多信息,并了解了如何根据自己的需要修改方法(为了与 PostgreSQL 语法中的问题相同):

DSL.field( "to_json(array_agg(DISTINCT author.*))", JSON.class, DSL.arrayAgg(AUTHOR.AUTHOR_ID) ).as("authors")

【讨论】:

在最后一个示例中,您不需要传递DSL.arrayAgg() 表达式,因为您没有任何模板占位符可以匹配它(例如0)。但是,如果需要,您仍然可以传递 AUTHOR 表,例如DSL.field("to_json(array_agg(distinct 0.*))", JSON.class, AUTHOR)

以上是关于如何在 jOOQ 中转换“to_json()”PostgreSQL 函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 jOOQ 中编写计数查询

如何在 JOOQ 中使用 toChar 函数?

如何在 jooq 中将数据转换为 Result<Record> 数据类型

如何使用 jOOQ gradle 插件将 postgres 中的 bigint[] 字段转换为类字段

如何查看 jOOQ 在编译时执行的 SQL 语句?

PostgreSQL 函数 + 在 jOOQ 中强制转换