如何使用 JOOQ 在 PostgreSQL 中插入带有 JSON 列的可更新记录?

Posted

技术标签:

【中文标题】如何使用 JOOQ 在 PostgreSQL 中插入带有 JSON 列的可更新记录?【英文标题】:How to insert a updatable record with JSON column in PostgreSQL using JOOQ? 【发布时间】:2015-01-18 14:46:28 【问题描述】:

我关注了Is it possible to write a data type Converter to handle postgres JSON columns?中的答案 实现nodeObject转换器。

然后我尝试使用可更新记录插入记录,我得到“org.jooq.exception.SQLDialectNotSupportedException: Type class org.postgresql.util.PGobject is not supported in dialect POSTGRES”异常。”

我该如何解决这个问题?

以下是我的代码:

TableRecord r = create.newRecord(TABLE);
ObjectNode node = JsonNodeFactory.instance.objectNode();
r.setValue(TABLE.JSON_FIELD, node, new JsonObjectConverter());
r.store();

【问题讨论】:

在 jOOQ 3.5(刚刚发布)之前,PostgreSQL 的 JSON 类型很难与 jOOQ 集成。我们现在改变了这一点。 Some preliminary information can be seen here on this user group thread。我们将很快更新手册等,我将详细回答这个问题。可以肯定的是,您使用的是 Jackson,对吗? 是的,我正在使用 Jackson。 【参考方案1】:

当前的 jOOQ 版本

jOOQ 原生支持 JSONJSONB 数据类型,因此您无需执行任何特定操作。

历史答案

从 jOOQ 3.5 开始,您可以将自己的自定义数据类型绑定注册到代码生成器,如下所述:

http://www.jooq.org/doc/latest/manual/code-generation/custom-data-type-bindings

Converter 不同,Binding 指示如何在 jOOQ 中的 JDBC 级别处理您的数据类型,而 jOOQ 不知道您的实现。即,您不仅要定义如何在 <T><U> 类型之间进行转换(T = 数据库类型,U = 用户类型),而且您还可以定义这些类型的方式:

呈现为 SQL 绑定到 PreparedStatements 绑定到 SQLOutput 在 CallableStatements 中注册为 OUT 参数 从结果集中获取 从 SQLInput 获取 作为 OUT 参数从 CallableStatements 中提取

这里给出了一个示例 Binding 与 Jackson 一起使用以生成 JsonNode 类型:

public class PostgresJSONJacksonJsonNodeBinding 
implements Binding<Object, JsonNode> 

    @Override
    public Converter<Object, JsonNode> converter() 
        return new PostgresJSONJacksonJsonNodeConverter();
    

    @Override
    public void sql(BindingSQLContext<JsonNode> ctx) throws SQLException 

        // This ::json cast is explicitly needed by PostgreSQL:
        ctx.render().visit(DSL.val(ctx.convert(converter()).value())).sql("::json");
    

    @Override
    public void register(BindingRegisterContext<JsonNode> ctx) throws SQLException 
        ctx.statement().registerOutParameter(ctx.index(), Types.VARCHAR);
    

    @Override
    public void set(BindingSetStatementContext<JsonNode> ctx) throws SQLException 
        ctx.statement().setString(
            ctx.index(), 
            Objects.toString(ctx.convert(converter()).value()));
    

    @Override
    public void get(BindingGetResultSetContext<JsonNode> ctx) throws SQLException 
        ctx.convert(converter()).value(ctx.resultSet().getString(ctx.index()));
    

    @Override
    public void get(BindingGetStatementContext<JsonNode> ctx) throws SQLException 
        ctx.convert(converter()).value(ctx.statement().getString(ctx.index()));
    

    // The below methods aren't needed in PostgreSQL:

    @Override
    public void set(BindingSetSQLOutputContext<JsonNode> ctx) throws SQLException 
        throw new SQLFeatureNotSupportedException();
    

    @Override
    public void get(BindingGetSQLInputContext<JsonNode> ctx) throws SQLException 
        throw new SQLFeatureNotSupportedException();
    

上面使用的Converter可以在这里看到:

public class PostgresJSONJacksonJsonNodeConverter 
implements Converter<Object, JsonNode> 
    @Override
    public JsonNode from(Object t) 
        try 
            return t == null 
              ? NullNode.instance 
              : new ObjectMapper().readTree(t + "");
        
        catch (IOException e) 
            throw new RuntimeException(e);
        
    

    @Override
    public Object to(JsonNode u) 
        try 
            return u == null || u.equals(NullNode.instance) 
              ? null 
              : new ObjectMapper().writeValueAsString(u);
        
        catch (IOException e) 
            throw new RuntimeException(e);
        
    

    @Override
    public Class<Object> fromType() 
        return Object.class;
    

    @Override
    public Class<JsonNode> toType() 
        return JsonNode.class;
    

您现在可以通过代码生成器配置注册上述绑定:

<customType>
    <name>com.example.PostgresJSONJacksonJsonNodeBinding</name>
    <type>com.fasterxml.jackson.databind.JsonNode</type>
    <binding>com.example.PostgresJSONJacksonJsonNodeBinding</binding>
</customType>

<forcedType>
    <name>com.example.PostgresJSONJacksonJsonNodeBinding</name>
    <expression>my_schema\.table\.json_field</expression>
</forcedType>

【讨论】:

谢谢卢卡斯!您的解决方案和 v3.5 来得正是时候! @Lukas,如果我们不知道目标类型,我们想将任何 JSON 对象绑定到我们碰巧拥有的任何结果类型对象怎么办?转换器的所有示例,类型绑定是特定的 DB-Type =&gt; User-Type 而不是 DB-Type =&gt; ? (所有 postgres 对象到我们现在想要的任何东西) @JaysonMinard:很难说。恐怕我不完全理解你的问题。也许您可以在 Stack Overflow 或user group 上提出一个新问题并进行一些解释?那我很乐意回答。 @LukasEder 我没有使用自动生成的代码,我需要 GSON 我该怎么办? @LukasEder 完成,***.com/questions/55595910/…

以上是关于如何使用 JOOQ 在 PostgreSQL 中插入带有 JSON 列的可更新记录?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 jOOQ 更新 PostgreSQL 上复合列的单个子字段?

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

使用 jOOQ 在 PostgreSQL 中进行 UPSERT

如何使用 jOOQ 执行特定查询

jOOQ 无法映射 PostgreSQL 列以使 JPA 满意

JOOQ快速上手(基于springboot 和 postgresql)