简体   繁体   English

JOOq:更新广义表

[英]JOOq: update for generalized table

I want to create a generalized method with JOOq that updates a table (specified by a string) using values from a JSON object.我想使用 JOOq 创建一个通用方法,该方法使用来自 JSON 对象的值更新表(由字符串指定)。 I'm not including any validation of tables/fields in this example.在此示例中,我不包括对表/字段的任何验证。

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));
    }
}

But I get a compile time error: no suitable method found for set(Field<CAP#1>,CAP#2) .但是我得到一个编译时错误: no suitable method found for set(Field<CAP#1>,CAP#2)

I think the problem is that the compiler doesn't know that the type of the field and the type of the value will be the same (hence CAP#1 and CAP#2).我认为问题在于编译器不知道字段的类型和值的类型是相同的(因此是 CAP#1 和 CAP#2)。

Is there any way to accomplish this?有什么办法可以做到这一点吗?

I think the problem is that the compiler doesn't know that the type of the field and the type of the value will be the same (hence CAP#1 and CAP#2). 我认为问题在于编译器不知道字段的类型和值的类型是否相同(因此CAP#1和CAP#2)。

That is the exact problem. 那是确切的问题。 Two different uses of the same wildcard type give rise to two different new capture types. 相同通配符类型的两种不同用法会产生两种不同的新捕获类型。

The solution is to introduce a small method, where the wildcard type is used once and bound to a type parameter. 解决方案是引入一个小的方法,该方法一次使用通配符类型并将其绑定到类型参数。 When it is bound to a type parameter the compiler recognises that different uses of it refers to the same type. 当将其绑定到类型参数时,编译器会认识到对它的不同用法是指同一类型。

Like this: 像这样:

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));
}

Another solution 另一种解决方案

...is to cast the field type to some concrete type: ...是将字段类型转换为某些具体类型:

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

That is less code to type, and in this simple example it works fine. 键入的代码更少,在这个简单的示例中,它可以正常工作。 But it is also a less type safe, so in more complex code it is probably better to introduce a method with a type parameter. 但是它的类型安全性也较差,因此在更复杂的代码中,最好引入带有类型参数的方法。

For example, the following type checks but probably crashes at runtime: 例如,以下类型检查,但可能在运行时崩溃:

update.set(field, entry);

A third and interesting solution 第三个有趣的解决方案

...would be to be able to declare a local type variable for the Field : ...将能够为Field声明一个局部类型变量:

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

But of course this is not legal Java, type variables can only be introduced as parameters on classes and methods, not on local variables. 但这当然不是合法的Java,类型变量只能作为类和方法的参数引入,而不能作为局部变量引入。


A fourth solution, using lambdas 第四个解决方案,使用lambdas

...is to define a util method and pass a lambda object to it. ...用于定义util方法,并将lambda对象传递给该方法。 It works the same way as the first solution, but you don't have to create a custom method every thing you want to do this. 它的工作方式与第一个解决方案相同,但是您不必为要执行的每件事都创建一个自定义方法。

// 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);
}

A variation of this is to use a some existing methods for the same result: 一种变化是使用一些现有方法来获得相同的结果:

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

The simplest solution would be to use the UpdateSetStep.set(Map<? extends Field<?>, ?>) method for this. 最简单的解决方案是为此使用UpdateSetStep.set(Map<? extends Field<?>, ?>)方法。 It applies rather relaxed type safety doing the data type conversion for you (if possible): 它为您提供了相当宽松的类型安全性,可以为您执行数据类型转换(如果可能):

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();
}

Through the above solution i came up with this kindof shortcut通过上述解决方案,我想出了这种捷径

  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();
  }

In Quarkus @Inject DSLContext dslContext in your class在类中的 Quarkus @Inject DSLContext dslContext

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM