如何使用Jooq根据包含的工作Java代码使用Kotlin创建动态条件生成器?

How can I use Jooq to create a dynamic Condition generator using Kotlin based on included working Java code?

提问人:Chris 提问时间:11/15/2023 更新时间:11/15/2023 访问量:27

问:

我想将Jooq与Kotlin语言一起使用,以创建一种动态生成Jooq Condition对象的方法。

我想将Jooq Field对象放在从String到TableField(Jooq类)的Kotlin地图中。我想从这个映射中提取值,并使用它们来构建Condition对象。

我有一个简单的概念证明,可以用 Java 进行编码,以单元测试的形式进行编码。该代码如下所示(省略了导入和类,但这就是这个想法。

    private static final Map<String, TableField> FIELDS = Map.of(
            "tagId", TagV2Table.Companion.getTAG_V2().getTAG_ID(),
            "createdDate", ApplicationTable.Companion.getAPPLICATION().getCREATED_DATE()
    );

    @Test
    void testDynamicGeneration() {
        TableField idField = FIELDS.get("tagId");
        TableField createTimeField = FIELDS.get("createdDate");

        final Condition condition = noCondition()
                .and(idField.eq("myId"))
                .and(createTimeField.lt(Instant.ofEpochMilli(123456)));

        final String expected = "(\n" +
                "  \"public\".\"tag_v2\".\"tag_id\" = 'myId'\n" +
                "  and \"public\".\"application\".\"created_date\" < timestamp with time zone '1970-01-01 00:02:03.456+00:00'\n" +
                ")";
        assertEquals(expected, condition.toString());

创建的条件是“预期”中的条件,这是我想要的。

如果我尝试将相同的代码翻译成 Kotlin,我会得到编译错误,我很确定这是由于 Map 上的“out”造成的。但是我知道的还不够多,不知道如何解决它。这是我编写的等效 Kotlin 代码:

    private companion object {
        // The IntelliJ inferred type is:
        //  Map<String, TableField<out UpdatableRecordImpl<*>, out Any?>>
        // AFAICT, the "out" of "out Any?" is making the ".eq" and ".lt" functions unavailable
        // because it's unsafe.
        val fields = mapOf(
            "tagId" to TAG_V2.TAG_ID,
            "createdDate" to APPLICATION.CREATED_DATE,
        )
    }

    @Test
    fun testDynamicGenerationKotlin() {
        val idField = fields["tagId"]
        val createTimeField = fields["createdDate"]

        val condition = noCondition()
            // .eq and .lt "None of the following functions can be called with the arguments supplied"
            // and the accepted arguments are all Nothing
            .and(idField.eq("myId"))
            .and(createTimeField.lt(Instant.ofEpochMilli(123456)))
    }

问题在于函数在对象上不可用,并且会从地图中拉出。.eq(...).lt(...)idFieldcreateTimeField

有没有办法让我声明这个 Map,以便 Kotlin 代码能够工作(就像 Java 代码一样)?

java sql kotlin 泛型 jooq

评论


答:

0赞 Lukas Eder 11/15/2023 #1

解决型式安全问题

Kotlin 没有 Java 的原始类型等价物,但你总是可以像这样不安全的强制转换:

.and((idField as Field<String>).eq("myId"))
.and((createTimeField as Field<Instant>).lt(Instant.ofEpochMilli(123456)))

或者,您不使用简单的映射,而是编写一个更复杂的实用程序(包装映射)来处理键入。

解决实际问题的替代方法

您的实际问题似乎是在您的架构中具有可重用的列。您可以声明一个接口,从任何表返回这些列,并让生成的表使用生成器策略或匹配器策略扩展此接口。

F.D.(英语:F.D.)

interface MyJooqTable<R : Record> : Table<R> {
    fun tagId(): Field<String> = field("tag_id", SQLDataType.STRING)
    fun createdDate(): Field<Instant> = field("CREATED_DATE", SQLDataType.INSTANT)
}

现在,您可以传递对实用程序的引用,并安全地键入取消引用列(假设这些列存在)。MyJooqTable

评论

0赞 Chris 11/15/2023
感谢您的回复!我已经在使用 Jooq 生成器来生成我的表类、字段等。例如,上面是 from 在生成的表类中。也许我之前应该更清楚:我需要做的是接受来自 API 的字符串标识符,例如“tag_id”,并能够查找相应的 TableField 以在 WHERE 子句中动态使用。这就是查找 String to TableField Map 的原因。有没有一种类型安全的 Kotlin 方法可以做到这一点?TAG_IDTableField<TagV2Record, String?>createField(DSL.name("tag_id"), SQLDataType.CLOB.nullable(false), this, "")
0赞 Chris 11/16/2023
我能够使用未经检查的演员表来使其工作。我这样做了: .编译器警告: 。我不确定是否有更好的方法可以做到这一点(对任何想法持开放态度)。我的一个朋友告诉我要读 youtrack.jetbrains.com/issue/KT-41062/......了解更多背景知识。我还没有消化它,但它是 Kotlin 功能请求,所以不会解决这个问题。(API_STRING_TO_JOOQ_FIELD_MAP[it.fieldName]!! as Field<Any>).eq(it.value)Unchecked cast: TableField<out UpdatableRecordImpl<*>, *> to Field<Any>