没有自定义转换器的 jOOQ 查询表达式类型安全

jOOQ query expression type-safety without custom converter

我正在尝试使用 Kotlin 的扩展函数以类型安全的方式使用 PostgreSQL 的全文搜索扩展 jOOQ。

我的问题是 DSL.function 没有 "know" 我的自定义 classes/types TsQuery 和 TsVector 并抛出异常。函数 class 本身没有 public 构造函数。

org.jooq.exception.SQLDialectNotSupportedException: Type class jooq.fulltext.TsVector is not supported in dialect DEFAULT

class TsQuery

class TsVector

fun Field<String>.toTsVector(searchConfig: String): Field<TsVector> {
    return DSL.function(
        "to_tsvector",
        TsVector::class.java,
        DSL.inline(searchConfig),
        DSL.coalesce(this, "")
    )!!
}

fun String.toTsQuery(searchConfig: String): Field<TsQuery> {
    return DSL.function(
        "to_tsquery",
        TsQuery::class.java,
        DSL.inline(searchConfig),
        DSL.value(this)
    )!!
}

fun Field<TsVector>.tsMatches(query: Field<TsQuery>): Condition {
    return DSL.condition(
        "{0} @@ {1}",
        this,
        query
    )!!
}

fun Field<TsVector>.tsRank(query: Field<TsQuery>): Field<Double> {
    return DSL.function(
        "ts_rank",
        Double::class.java,
        this,
        query
    )!!
}

如果我将 TsQuery 和 TsVector 替换为 String 则它可以工作,但我松散了输入。 我只想将它们用于查询构建,我不需要能够 parse/convert 这些类型 to/from Kotlin。

即使不创建自定义转换器,也可以直接创建 DefaultDataType 对象。

fun Field<String>.toTsVector(searchConfig: String): Field<TsVector> {

    return DSL.function(
        "to_tsvector",
        DefaultDataType(
            SQLDialect.POSTGRES,
            TsVector::class.java,
            "ts_vector"
        ),
        DSL.inline(searchConfig),
        DSL.coalesce(this, "")
    )!!
}

fun String.toTsQuery(searchConfig: String): Field<TsQuery> {
    return DSL.function(
        "to_tsquery",
        DefaultDataType(
            SQLDialect.POSTGRES,
            TsQuery::class.java,
            "ts_vector"
        ),
        DSL.inline(searchConfig),
        DSL.value(this)
    )!!
}

fun Field<TsVector>.tsMatches(query: Field<TsQuery>): Condition {
    return DSL.condition(
        "{0} @@ {1}",
        this,
        query
    )!!
}

fun Field<TsVector>.tsRank(query: Field<TsQuery>): Field<Double> {
    return DSL.function(
        "ts_rank",
        Double::class.java,
        this,
        query
    )!!
}

我在这上面花的时间比我应该花的多得多,而且我没有看到 the jOOQ manual 中提到的 DefaultDataType,所以可能有更好的方法我不知道。

虽然您可以使用内部 类 使其工作,例如 DefaultDataType (),但您不应该这样做,因为您的解决方案可能会在未来的任何次要版本中中断,甚至补丁发布。

在 jOOQ 中引入对新数据类型的支持的正确方法是实现 a converter or even better, a binding。绑定将允许您影响 jOOQ 如何将变量绑定到准备好的语句,以及它如何从结果集中读取它们。

您的 toTsVector() 方法将如下所示(请原谅任何 Kotlin 错误,我是 Java 人):

fun Field<String>.toTsVector(searchConfig: String): Field<TsVector> {
    return DSL.function(
        "to_tsvector",
        SQLDataType.OTHER.asConvertedDataType(MyTsVectorBinding()),
        DSL.inline(searchConfig),
        DSL.coalesce(this, "")
    )!!
}

您现在可以实现(我使用 Java 因为缺少 kotlin-fu,但这没关系):

class MyTsVectorBinding implements Binding<Object, TsVector> {
    // ...
}