JOOQ:方言 DEFAULT 不支持类型 class org.jooq.impl.SelectImpl

JOOQ: Type class org.jooq.impl.SelectImpl is not supported in dialect DEFAULT

我关于在 jooq dsl 中编写查询的问题。

我在 Oracle 数据库中存储了一些客户端属性列表。

Table结构如下:

我正在尝试 select 给定客户端的所有现有属性(字典中)的值:

如果 CLIENT_ATTRIBUTE 中存在具有给定 clientId 的行而不是属性值 = CLIENT_ATTRIBUTE.VALUE 否则 CLIENT_ATTRIBUTE_DICT.DEFAULT_VALUE

我在 SQL 中的查询(工作正常):

SELECT d.code,
  NVL 
 (
  (
   SELECT value
   FROM CLIENT_ATTRIBUTE a
   WHERE a.ATTRIBUTE_ID = d.id
   AND a.CLIENT_ID      = 1 
  ),
  ( 
   SELECT DEFAULT_VALUE 
   FROM CLIENT_ATTRIBUTE_DICT dd 
   WHERE dd.id=d.id
  ) 
 ) value
FROM CLIENT_ATTRIBUTE_DICT d;

我在 Jooq dsl 中的查询:

ClientAttributeDictTable dict = CLIENT_ATTRIBUTE_DICT.as("d");
Map<String, String> attributes = 
dsl.select(
 dict.CODE,
 DSL.nvl(
        dsl.select(CLIENT_ATTRIBUTE.VALUE)
           .from(CLIENT_ATTRIBUTE)
           .where(
                  CLIENT_ATTRIBUTE.ATTRIBUTE_ID.eq(dict.ID),
                  CLIENT_ATTRIBUTE.CLIENT_ID.eq(clientId)
                 ),

        dsl.select(CLIENT_ATTRIBUTE_DICT.DEFAULT_VALUE)
           .from(CLIENT_ATTRIBUTE_DICT)
           .where(CLIENT_ATTRIBUTE_DICT.ID.eq(dict.ID))
       ).as("value")
    ).from(dict)
     .fetchMap(String.class, String.class);

当 jooq 查询运行时失败并显示错误消息:

Caused by: Type class org.jooq.impl.SelectImpl is not supported in dialect DEFAULT

at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:757) ~[na:na]
at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:704) ~[na:na]
at org.jooq.impl.DSL.getDataType(DSL.java:14378) ~[na:na]
at org.jooq.impl.DSL.val(DSL.java:12766) ~[na:na]
at org.jooq.impl.Utils.field(Utils.java:802) ~[na:na]
at org.jooq.impl.DSL.nvl(DSL.java:8403) ~[na:na]

我做错了什么?

UPD JOOQ版本3.7.2

我找到了解决方法。

首先我重写了 SQL 查询:

SELECT D.CODE, NVL(S.VALUE, D.DEFAULT_VALUE) ATTRIBUTE_VALUE
FROM CLIENT_ATTRIBUTE_DICT D
LEFT JOIN
  (SELECT DD.ID,
    A.VALUE
  FROM CLIENT_ATTRIBUTE_DICT DD
  JOIN CLIENT_ATTRIBUTE A
  ON A.ATTRIBUTE_ID = DD.ID
  WHERE A.CLIENT_ID = 1
  ) S ON S.ID       = D.ID;

比起我在 JOOQ dsl 中表达我的查询:

String ID_FIELD_NAME    = "ID";
String VALUE_FIELD_NAME = "VALUE"

ClientAttributeDictTable DICT_ATTRIBUTES = CLIENT_ATTRIBUTE_DICT.as("D");
Table<Record2<Long, String>> EXISTING_ATTRIBUTES =
    dsl.select(CLIENT_ATTRIBUTE_DICT.ID, CLIENT_ATTRIBUTE.VALUE)
       .from(CLIENT_ATTRIBUTE_DICT)
       .join(CLIENT_ATTRIBUTE)
       .on(CLIENT_ATTRIBUTE.ATTRIBUTE_ID.eq(CLIENT_ATTRIBUTE_DICT.ID))
       .where(CLIENT_ATTRIBUTE.CLIENT_ID.eq(clientId))
       .asTable("S", ID_FIELD_NAME, VALUE_FIELD_NAME);

Field<String> ATTRIBUTE_VALUE_FIELD = 
    nvl(
        EXISTING_ATTRIBUTES.field(VALUE_FIELD_NAME, String.class),
        DICT_ATTRIBUTES.DEFAULT_VALUE
    ).as("ATTRIBUTE_VALUE");

Map<String,String> attributes = 
    dsl.select(DICT_ATTRIBUTES.CODE, ATTRIBUTE_VALUE_FIELD)
        .from(DICT_ATTRIBUTES)
        .leftJoin(EXISTING_ATTRIBUTES)
        .on(DICT_ATTRIBUTES.ID
               .eq(EXISTING_ATTRIBUTES.field(ID_FIELD_NAME, Long.class))
           )
        .fetchMap(DICT_ATTRIBUTES.CODE, ATTRIBUTE_VALUE_FIELD); 

在日志中,我发现 JOOK 生成的 SQL 查询发生了一点变化:

SELECT D.CODE,
  NVL(S.VALUE, D.DEFAULT_VALUE) ATTRIBUTE_VALUE
FROM CLIENT_ATTRIBUTE_DICT D
LEFT OUTER JOIN (
  (SELECT NULL ID, NULL VALUE FROM dual WHERE 1 = 0
  )
UNION ALL
  (SELECT CLIENT_ATTRIBUTE_DICT.ID,
    CLIENT_ATTRIBUTE.VALUE
   FROM CLIENT_ATTRIBUTE_DICT
   JOIN CLIENT_ATTRIBUTE
   ON CLIENT_ATTRIBUTE.ATTRIBUTE_ID = CLIENT_ATTRIBUTE_DICT.ID
   WHERE CLIENT_ATTRIBUTE.CLIENT_ID = 141
  )
 ) S ON D.ID = S.ID;

感谢任何改进我的解决方法的想法。

错误有两方面。

  1. 您的 API 用法目前不受支持(jOOQ 目前不接受 nvl() 函数中的 Select 类型)。改为这样写:

    DSL.nvl(
        DSL.field(dsl.select(CLIENT_ATTRIBUTE.VALUE)
    //  ^^^^^^^^^ explicitly wrap the Select in a Field 
           .from(CLIENT_ATTRIBUTE)
           .where(
                  CLIENT_ATTRIBUTE.ATTRIBUTE_ID.eq(dict.ID),
                  CLIENT_ATTRIBUTE.CLIENT_ID.eq(clientId))),
    
        DSL.field(dsl.select(CLIENT_ATTRIBUTE_DICT.DEFAULT_VALUE)
    //  ^^^^^^^^^ explicitly wrap the Select in a Field
           .from(CLIENT_ATTRIBUTE_DICT)
           .where(CLIENT_ATTRIBUTE_DICT.ID.eq(dict.ID)))
    ).as("value")
    
  2. jOOQ(或者更确切地说,Java 编译器)无法检测到这种 API 误用,因为 nvl() API 在某种程度上被重载了总是编译。这是一个问题 https://github.com/jOOQ/jOOQ/issues/5340,将在未来的版本中修复(可能是 jOOQ 3.9)