spring-boot-data-r2dbc-jooq icon indicating copy to clipboard operation
spring-boot-data-r2dbc-jooq copied to clipboard

Add support for queries using `*ResultStep#returningResult`

Open GeorgiPetkov opened this issue 3 years ago • 2 comments

In JOOQ there are 2 general ways to declare the returning clause (for update/insert/delete). Those are [Insert|Update|Delete]ResultStep#returning and [Insert|Update|Delete]ResultStep#returningResult where the latter is to allow returning different record type than the one representing the table (e.g. Record1<UUID> instead of UserRecord with just one field actually present (if that's all you need)).

Currently only the first approach is supported. It would be useful to support the latter as well because it currently fails with ClassCastException at runtime. Example output: java.lang.ClassCastException: class com...model.tables.records.UserRecord cannot be cast to class org.jooq.Record1

GeorgiPetkov avatar May 07 '21 10:05 GeorgiPetkov

it currently fails with ClassCastException at runtime. Example output: java.lang.ClassCastException: class com...model.tables.records.UserRecord cannot be cast to class org.jooq.Record1

What is the behaviour of JOOQ in this case? Can you please provide a code sample that results in this error?

gofabian avatar May 09 '21 18:05 gofabian

The problems is that the method ReactiveJooq#executeReturning (called by ReactiveJooq#executeReturningOne below) always creates records of table's type. In other words, the assumption that the insert/update query's generic type matches the record type of the table is incorrect when using returningResult. I've provided 2 tests showing the differences (the second one fails). I've extracted the queries into variables to emphasize on the types. Apart from the last line in the query and the map usage the tests are identical.

    @Test
    void testWithReturning() {
        InsertResultStep<ProcessedEventsRecord> insert = dslContext
                .insertInto(PROCESSED_EVENTS)
                .set(PROCESSED_EVENTS.ID, randomUUID())
                .set(PROCESSED_EVENTS.CONTENT, "content")
                .returning(PROCESSED_EVENTS.ID);

        executeReturningOne(insert)
                .map(ProcessedEventsRecord::getId)
                .block(); // works
    }

    @Test
    void testWithReturningResult() {
        InsertResultStep<Record1<UUID>> insert = dslContext
                .insertInto(PROCESSED_EVENTS)
                .set(PROCESSED_EVENTS.ID, randomUUID())
                .set(PROCESSED_EVENTS.CONTENT, "content")
                .returningResult(PROCESSED_EVENTS.ID);

        executeReturningOne(insert)
                .map(Record1::value1)
                .block(); // ClassCastException
    }

Where the table in use is generated from:

CREATE TABLE processed_events
(
    id      UUID PRIMARY KEY,
    content VARCHAR NOT NULL
);

GeorgiPetkov avatar May 10 '21 20:05 GeorgiPetkov