JOOq:通用表的更新

Posted

技术标签:

【中文标题】JOOq:通用表的更新【英文标题】:JOOq: update for generalized table 【发布时间】:2017-06-29 09:34:33 【问题描述】:

我想使用 JOOq 创建一个 generalized 方法,该方法使用 JSON 对象中的值更新表(由字符串指定)。在此示例中,我不包括对表/字段的任何验证。

public void updateTable(String table, JsonObject data) 
    Table<?> table = PUBLIC.getTable(table);

    UpdateSetFirstStep<?> update = DSL.using(fooConfig).update(table);

    // Loop through JSON field1: value1, field2: value2, ...
    for (Map.Entry<String, Object> entry : data) 
        String fieldName = entry.getKey();
        Field<?> field = table.field(fieldName);

        Object value = entry.getValue();

        // error: no suitable method found for set(Field<CAP#1>,CAP#2)
        update.set(field, field.getType().cast(value));
    

但我得到一个编译时错误:no suitable method found for set(Field&lt;CAP#1&gt;,CAP#2)

我认为问题在于编译器不知道字段的类型和值的类型将相同(因此 CAP#1 和 CAP#2)。

有没有办法做到这一点?

【问题讨论】:

【参考方案1】:

我认为问题在于编译器不知道字段的类型和值的类型将相同(因此 CAP#1 和 CAP#2)。

这正是问题所在。同一通配符类型的两种不同用途会产生两种不同的新捕获类型。

解决方案是引入一个小方法,其中通配符类型被使用一次并绑定到类型参数。 当它绑定到类型参数时,编译器会识别出它的不同用途是指为同一类型。

像这样:

public void updateTable(String name, JsonObject data) 
    Table<?> table = PUBLIC.getTable(name);

    UpdateSetFirstStep<?> update = DSL.using(fooConfig).update(table);

    // Loop through JSON field1: value1, field2: value2, ...
    for (Map.Entry<String, Object> entry : data) 
        String fieldName = entry.getKey();
        Field<?> field = table.field(fieldName);

        Object value = entry.getValue();

        // Here the wildcard type is bound to the
        // type variable of the updateField method
        updateField(update, field, value);
    


public <T> void updateField(UpdateSetStep<?> update, Field<T> field, Object value) 
    // When the wildcard is bound to T it can be used
    // multiple times without a problem
    update.set(field, field.getType().cast(value));


另一种解决方案

...是将字段类型转换为某种具体类型:

@SuppressWarnings("unchecked")
Field<Object> field = (Field<Object>) table.field(fieldName);
update.set(field, field.getType().cast(entry.getValue()));

输入的代码更少,在这个简单的例子中它工作得很好。但它的类型安全性也较低,因此在更复杂的代码中,引入带类型参数的方法可能会更好。

例如,以下类型检查但可能在运行时崩溃:

update.set(field, entry);

第三种有趣的解决方案

...应该能够为Field 声明一个局部类型变量:

<T> Field<T> field = table.field(fieldName);

但这当然是不合法的Java,类型变量只能作为参数引入类和方法,不能引入局部变量。


第四个解决方案,使用 lambdas

...是定义一个util方法并传递一个lambda对象给它。它的工作方式与第一个解决方案相同,但您不必为每件事都创建自定义方法。

// Loop through JSON field1: value1, field2: value2, ...
for (Map.Entry<String, Object> entry : data) 
    String fieldName = entry.getKey();
    Field<?> field = table.field(fieldName);

    Object value = entry.getValue();

    captureType(field, f -> update.set(f, f.getType().cast(value)));


public static <T> void captureType(T o, Consumer<T> c) 
    c.accept(o);

对此的一种变体是使用一些现有的方法来获得相同的结果:

Optional.of(field).ifPresent(f -> update.set(f, f.getType().cast(value)));

【讨论】:

【参考方案2】:

最简单的解决方案是为此使用UpdateSetStep.set(Map&lt;? extends Field&lt;?&gt;, ?&gt;) 方法。它应用相当宽松的类型安全性为您进行数据类型转换(如果可能):

public void updateTable(String table, JsonObject data) 
    Table<?> table = PUBLIC.getTable(table);

    DSL.using(fooConfig)
       .update(table)
       .set(data.entrySet()
                .stream()
                .map(e -> new SimpleImmutableEntry(table.field(e.getKey()), e.getValue()))
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue)))
       .where(...) // Don't forget this! ;-)
       .execute();

【讨论】:

【参考方案3】:

通过上述解决方案,我想出了这种捷径

  public void update(final String db, final String tbl, final Map<Field<?>, ?> data, final Condition condition) 
    dslContext.update(DSL.table(db + "." + tbl)).set(data).where(condition).execute();
  

在 Quarkus @Inject DSLContext dslContext 你的班级

【讨论】:

感谢您的回答。但是您应该进一步详细说明为什么您的答案有价值并带来与问题的其他答案不同的东西。

以上是关于JOOq:通用表的更新的主要内容,如果未能解决你的问题,请参考以下文章

如何在使用 jooq 生成的 dao 插入/更新后获取插入/更新的对象

JOOQ - 更新新添加的列

在更新语句中排除空列 - JOOQ

尝试更新到“org.jooq:jooq-codegen-maven:3.15.1”时构建失败

使用 JOOQ 更新 PostgreSQL 中的 jsonb 值

Jooq 使用 MySql 中的记录批量更新