JOOq: 通用表格的更新

4
我想使用JOOq创建一个通用方法,从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<CAP#1>,CAP#2)

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

有没有办法解决这个问题?

3个回答

2
我认为问题在于编译器不知道字段的类型和值的类型将是相同的(因此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语法,类型变量只能作为类和方法的参数引入,而不是局部变量。

第四种解决方案,使用lambda表达式

...是定义一个工具方法并将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)));

1
最简单的解决方法是使用UpdateSetStep.set(Map<? extends Field<?>, ?>)方法。它应用了相对宽松的类型安全性,为您执行数据类型转换(如果可能)。
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();
}

0

通过以上解决方案,我想出了这种快捷方式。

  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

谢谢您的回答。但是,您应该进一步详细说明为什么您的答案具有价值,并且与其他答案带来了不同的东西。 - Patrick

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