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

10

我按照 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" exception." 的异常信息。

我该如何解决这个问题?

以下是我的代码:

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 集成相当困难。我们现在已经改变了这一点。此用户组线程中可以看到一些初步信息。我们很快会更新手册等内容,并用详细信息回答这个问题。只是为了确认,您正在使用 Jackson 吗? - Lukas Eder
是的,我正在使用Jackson。 - ChromeHearts
1个回答

8

当前 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 上
  • 作为 OUT 参数在 CallableStatements 中注册
  • 从 ResultSets 中提取
  • 从 SQLInput 中提取
  • 作为 OUT 参数从 CallableStatements 中提取

下面是一个使用 Jackson 生成 JsonNode 类型的示例 Binding:

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>

谢谢Lukas!你的解决方案和v3.5来得正是时候! - ChromeHearts
@Lukas,如果我们不知道目标类型,我们想将任何JSON对象绑定到我们作为结果类型对象的任何内容中,该怎么办?在所有转换器的示例中,类型绑定都是特定的DB-Type => User-Type,而不是DB-Type => ?(将所有postgres对象转换成我们此刻想要的任何类型)。 - Jayson Minard
@JaysonMinard:很难说。恐怕我没有完全理解你的问题。也许你可以在Stack Overflow上或者user group上提出一个带有一些解释的新问题?那时我会非常乐意回答的。 - Lukas Eder
@LukasEder 我没有使用自动生成的代码,但我需要 GSON,我该怎么办? - nairavs
@nairavs:提出一个新问题 :) - Lukas Eder
1
@LukasEder完成,https://stackoverflow.com/questions/55595910/issue-with-jooq-json-binding - nairavs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接