Alexander Ioffe
12/01/2025, 3:30 PMHristijan
12/01/2025, 4:28 PMAlexander Ioffe
12/01/2025, 6:11 PMcolumnA == columnB or if (columnA == X) Y else Z you need to use DSL constructs like columnA eq columnB or Case.When(columnA eq X).Then(Y).Else(Z).
This is a much bigger problem when you start joining many tables and doing anything that involves any sort of complexity. For example, here's a ExoQuery query that looks like something you'll see in the wild:
sql.select {
val c = from(Table<Customer>())
val o = join(Table<Order>()) { o -> o.customerId == c.id }
val oi = join(Table<OrderItem>()) { oi -> oi.orderId == o.id }
where {
c.status == "ACTIVE" && o.orderDate >= param(startDate)
(oi.quantity > 0 && (oi.discountPercent == null || oi.discountPercent <= param(BigDecimal("50"))))
}
// Grouping keys
groupBy(c.id, c.firstName, c.lastName, o.id, o.orderDate)
having {
val total = sum(oi.quantity.toBigDecimal() * oi.unitPrice)
total >= param(minTotal) && total <= param(maxTotal)
}
OrderSummary(
customerFullName = c.firstName + " " + c.lastName,
orderId = o.id,
orderDate = o.orderDate,
orderTotal = sum(oi.quantity.toBigDecimal() * oi.unitPrice),
distinctProducts = count(oi.productId)
)
}
It's complex, but comprehensible.
Now compare it to what the equivalent in JOOQ would be:
var c = CUSTOMER.as("c");
var o = ORDERS.as("o");
var oi = CUSTOMER_ORDER_ITEM.as("oi");
ctx.select(
c.FIRST_NAME.concat(inline(" ")).concat(c.LAST_NAME).as("customer_full_name"),
o.ORDER_DATE.as("order_date"),
sum(oi.QUANTITY.cast(BigDecimal.class).mul(oi.UNIT_PRICE)).as("order_total"),
countDistinct(oi.PRODUCT_ID).as("distinct_products")
)
.from(c)
.join(o).on(o.CUSTOMER_ID.eq(c.ID))
.join(oi).on(oi.ORDER_ID.eq(o.ID))
.where(
c.STATUS.eq("ACTIVE")
.and(o.ORDER_DATE.ge(LocalDate.now().minusMonths(3)))
.and(
oi.QUANTITY.cast(BigDecimal.class)
.mul(oi.UNIT_PRICE)
.gt(new BigDecimal("50.00"))
)
)
.groupBy(
c.FIRST_NAME, c.LAST_NAME, o.ID, o.ORDER_DATE
)
.orderBy(
sum(oi.QUANTITY.cast(BigDecimal.class).mul(oi.UNIT_PRICE)).desc()
)
It's much harder to read and heaven-forbid you get an alias wrong somewhere like doing oi.ORDER_ID.eq(oi.ID) instead of oi.ORDER_ID.eq(o.ID) .
Hunting down that sort of thing in prod can turn into a huge time-suck.Nathan Fallet
12/01/2025, 10:15 PMAlexander Ioffe
12/01/2025, 10:24 PMNathan Fallet
12/01/2025, 10:26 PMAlexander Ioffe
12/01/2025, 10:29 PMAlexander Ioffe
12/01/2025, 10:30 PMNathan Fallet
12/01/2025, 10:33 PM