使用jOOQ

时间:2018-10-21 16:18:58

标签: java postgresql jooq

上下文

我正在针对 PostgreSQL 数据库使用 jOOQ
我想在jsonb_object_agg(name, value)的结果集中使用LEFT OUTER JOIN

问题

联接是一个OUTER联接,有时聚合功能的name组件只是null:这是行不通的。然后我会去:

COALESCE(
    json_object_agg(table.name, table.value) FILTER (WHERE table.name IS NOT NULL),
    '{}'
)::json

到目前为止,我用来调用jsonb_object_agg的代码如下(不完全是,但归结为):

public static Field<?> jsonbObjectAgg(final Field<?> key, final Select<?> select) {
    return DSL.field("jsonb_object_agg({0}, ({1}))::jsonb", JSON_TYPE, key, select);
}

...,其中JSON_TYPE是:

private static final DataType<JsonNode> JSON_TYPE = SQLDataType.VARCHAR.asConvertedDataType(/* a custom Converter */);

解决方案不完整

我很想利用 jOOQ AggregateFilterStep界面,尤其是能够使用其AggregateFilterStep#filterWhere(Condition... conditions)

但是,org.jooq.impl.Function(通过implements AggregateFilterStepAgregateFunction间接访问的ArrayAggOrderByStep类的可见性受限于其package,所以我不能只是盲目地回收DSL#ArrayAggOrderByStep的实现:

public static <T> ArrayAggOrderByStep<T[]> arrayAgg(Field<T> field) {
    return new org.jooq.impl.Function<T[]>(Term.ARRAY_AGG, field.getDataType().getArrayDataType(), nullSafe(field));
}

尝试

我最接近合理的方法是...构建自己的coalesceAggregation函数,该函数专门合并聚合的字段:

//                                  Can't quite use AggregateFunction there
//                                                   v   v
public static <T> Field<T> coalesceAggregation(final Field<T> agg, final Condition coalesceWhen, @NonNull final T coalesceTo) {
    return DSL.coalesce(DSL.field("{0} FILTER (WHERE {1})", agg.getType(), agg, coalesceWhen), coalesceTo);
}

public static <T> Field<T> coalesceAggregation(final Field<T> agg, @NonNull final T coalesceTo) {
    return coalesceAggregation(agg, agg.isNotNull(), coalesceTo);
}

...但是我随后遇到了T类型为JsonNode的问题,其中DSL#coalesce似乎CAST我的coalesceTo到{{1 }}。

或者,您知道:

varchar

但这是最后的选择:感觉就像离让用户将他们想要的任何SQL注入我的数据库仅一步之遥

简而言之

jOOQ 中,有没有一种方法可以“正确地”实现自己的聚合函数,如实际的DSL.field("COALESCE(jsonb_object_agg({0}, ({1})) FILTER (WHERE {0} IS NOT NULL), '{}')::jsonb", JSON_TYPE, key, select)
我想尽可能避免由org.jooq.AgregateFunction生成它(不是我不喜欢它-只是我们的管道很恐怖)。

1 个答案:

答案 0 :(得分:1)

jOOQ DSL API中缺少一项功能,即创建plain SQL aggregate functions。之所以无法使用该功能(从jOOQ 3.11开始)是因为要指定支持所有特定于供应商的所有选项的供应商不可知聚合函数有很多微妙的内部因素,

  • FILTER (WHERE ...)子句(如您在问题中所述),必须使用CASE
  • 进行仿真
  • OVER (...)子句将聚合函数转换为窗口函数
  • WITHIN GROUP (ORDER BY ...)子句支持有序集合聚合函数
  • DISTINCT子句(受支持的地方)
  • 其他特定于供应商的扩展,以聚合功能

在您的特定情况下,一种简单的解决方法是也使用plain SQL templating,就像您在问题中提到的那样:

DSL.field("COALESCE(jsonb_object_agg({0}, ({1})) FILTER (WHERE {0} IS NOT NULL), '{}')::jsonb", JSON_TYPE, key, select)

或者您做您之前提到的事情。关于这个问题:

  

...但是后来我遇到了T型为JsonNode的问题,在这里DSL#coalesce似乎将我的coalesceTo转换为varchar。

那可能是因为您使用的是agg.getType()返回了Class<?>,而不是agg.getDataType()返回了DataType<?>

  

但这将是最后的手段:感觉就像离让用户将他们想要的任何SQL注入我的数据库仅一步之遥

我不确定为什么这是一个问题。您仍然可以自己控制普通SQL API的使用,并且用户也无法控制keyselect中的内容,因为您也可以控制这些元素。

相关问题