How can I map entity using a Jooq?

Viewed 263

I have a trouble: can't figure out how to map result of Jooq query.

I have 2 entities: Payment method and Currency for it. Payment method contains a List inside. Both are stored in different tables.

I get one:

public Optional<PaymentMethod> getMethod(Long id) {
return this.dslContext.selectFrom(PAYMENT_METHOD)
    .where(
        PAYMENT_METHOD.ID.eq(id),
        PAYMENT_METHOD.DELETED.eq(false)
    ).fetchOptional().map(v -> methodDao.mapper().map(v));

And want to get currencies too, by using a:

return this.dslContext.select(asterisk())
    .from(PAYMENT_METHOD)
    .join(PAYMENT_METHOD_CURRENCY)
    .on(PAYMENT_METHOD_CURRENCY.METHOD_ID.eq(id))
    .where(
        PAYMENT_METHOD.ID.eq(id),
        PAYMENT_METHOD.DELETED.eq(false)
    )
    .fetch()
    .map(...)...

So, how can I map this further?

I have a decision for a single entity inside other, like that:

private PaymentProviderMethod mapMethod(Record record) {
final PaymentMethodRecord methodRecord = record.into(PAYMENT_METHOD);
final PaymentProviderRecord providerRecord = record.into(PAYMENT_PROVIDER);
return new PaymentProviderMethod(
    methodDao.mapper().map(methodRecord),
    providerDao.mapper().map(providerRecord));

But have no idea how to map list in this case. Could u help, please?

2 Answers

If you want to nest collections in with jOOQ, then you're probably looking for the MULTISET or MULTISET_AGG operator along with ad-hoc conversion, which allows for type safe nesting collections directly in SQL, if your database product support SQL/XML or SQL/JSON.

The following might not be the exact query you were looking for, but you get the idea:

// Assuming the usual static import:
import static org.jooq.impl.DSL.*;

ctx.select(
       PAYMENT_METHOD.ID,
       PAYMENT_METHOD.PROVIDER_ID,
       PAYMENT_METHOD.NAME,
       ...
       multiset(
           select(
               PAYMENT_METHOD_CURRENCY.ID,
               PAYMENT_METHOD_CURRENCY.NAME,
               ...
           )
           .from(PAYMENT_METHOD_CURRENCY)
           .where(PAYMENT_METHOD_CURRENCY.METHOD_ID.eq(PAYMENT_METHOD.ID))
       ).convertFrom(r -> r.map(Records.mapping(Currency::new)))
   )
   .from(PAYMENT_METHOD)
   .fetch(Records.mapping(PaymentMethod::new))

The above example uses MULTISET for a nested, correlated subquery, but you coudl do the same thing with MULTISET_AGG and a JOIN and GROUP BY clause, especially if your dialect doesn't support correlating derived tables, see issue #12045. The whole query is type safe. If you change your PaymentMethod or Currency DTO classes, the query will stop compiling until you fix your projections.

If you use the Jooq version less than 13.15 then you cannot use multiset. And for me I had the same issue, and I solved it by manually mapping the records. I did that with Kotlin, but I think you can figure out the idea from there:

private fun mapRecords(records: Result<Record>): List<PaymentMethod> {
        val paymentMethodCurrencies = records.into(PaymentMethodCurrencyRecord::class.java)
            .map(::mapPaymentMethodCurrency)
            .groupBy { it.paymentMethodId }

        val paymentMethods = records.into(PaymentMethodRecord::class.java).distinct()
            .map { 
                mapPaymentMethod(it, paymentMethodCurrencies[it.id])
            }

        return paymentMethods
    }
Related