Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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).

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).

Is there any way to accomplish this?

like image 902
lebolo Avatar asked Oct 19 '25 02:10

lebolo


1 Answers

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).

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:

<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.


A fourth solution, using lambdas

...is to define a util method and pass a lambda object to it. 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)));
like image 193
Lii Avatar answered Oct 21 '25 16:10

Lii



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!