Kotlin выводит неправильные методы JOOQ - PullRequest
2 голосов
/ 25 февраля 2020

Для системы, использующей Kotlin версии 1.3.61 и JOOQ версии 3.13.1, такой метод обычно создает запрос union:

    val selectCommonPart = coalesce(sum(field(name("amount"), Long::class.java)), ZERO)
            .`as`(field(name("totalAmount")))
    var whereCommonPart: Condition = trueCondition().and(field(name("Id")).eq(Id)) // Id comes as a parameter

    var query = dsl.selectQuery()
    query.addSelect(selectCommonPart)
    query.addFrom(table("${tableName(startDate)}")) // `startDate` is a `LocalDate`, `tableName()` generates the table name as String
    query.addConditions(whereCommonPart)

    // `endDate` is also a `LocalDate`, can be either equal to `startDate` or after
    if (endDate.isAfter(startDate)) {
        for (date in Stream.iterate(startDate.plusDays(1), { d: LocalDate -> d.plusDays(1) })
                .limit(ChronoUnit.DAYS.between(startDate, endDate))
                .collect(Collectors.toList())) {

            val unionQuery = dsl.selectQuery()
            unionQuery.addSelect(selectCommonPart)
            unionQuery.addFrom(table("public.${tableName(date)}"))
            unionQuery.addConditions(whereCommonPart)

            // Here `union` is inferred correctly
            query.union(dsl.select(selectCommonPart)
                    .from("public.public.${tableName(date)}")
                    .where(whereCommonPart))
        }
    }

Однако, если я изолирую dsl.select(...) участвуйте в таком методе, как:

private fun buildSelect(selectCommonPart: Field<*>, whereCommonPart: Condition, date: LocalDate): Select<*> {
    var query = dsl.selectQuery()
    query.addSelect(selectCommonPart)
    query.addFrom(table("public.${tableName(date)}"))
    query.addConditions(whereCommonPart)
    return query
}

И измените l oop, чтобы использовать:

    // Remove this part
    /* var query = dsl.selectQuery()
    query.addSelect(selectCommonPart)
    query.addFrom(table("${tableName(startDate)}")) // `startDate` is a `LocalDate`, `tableName()` generates the table name as String
    query.addConditions(whereCommonPart) */

    // Add this part
    var query = buildSelect(selectCommonPart, whereCommonPart, startDate)

    if (endDate.isAfter(startDate)) {
        for (date in Stream.iterate(startDate.plusDays(1), { d: LocalDate -> d.plusDays(1) })
                .limit(ChronoUnit.DAYS.between(startDate, endDate))
                .collect(Collectors.toList())) {

            // This gives an inference error
            query.union(buildSelect(selectCommonPart, whereCommonPart, date))
        }
    }

У меня ошибка вывода. Kotlin разрешает union как этот метод:

/**
 * Returns a set containing all distinct elements from both collections.
 * 
 * The returned set preserves the element iteration order of the original collection.
 * Those elements of the [other] collection that are unique are iterated in the end
 * in the order of the [other] collection.
 * 
 * To get a set containing all elements that are contained in both collections use [intersect].
 */

public infix fun <T> Iterable<T>.union(other: Iterable<T>): Set<T> {
    val set = this.toMutableSet()
    set.addAll(other)
    return set
}

Я хочу использовать Select<*> JOOQ * вместо 1017 *:

public interface Select<R extends Record> extends ResultQuery<R>, TableLike<R>, FieldLike {

    /**
     * Apply the <code>UNION</code> set operation.
     */
    @Support
    Select<R> union(Select<? extends R> select);

Что я должен сделать, чтобы сделать вывод правильный union метод?

Ответы [ 2 ]

3 голосов
/ 27 февраля 2020

Хорошо, я узнал, как решить вашу проблему.

Метод buildSelect должен вернуть Выберите <Запись> , а не Выберите <*> .

Мое предположение, почему это происходит:

Метод Select.union имеет следующую подпись

public interface Select<R extends Record> extends ResultQuery<R>, TableLike<R>, FieldLike {
    @Support
    Select<R> union(Select<? extends R> var1);
....

Как видите, var1 должен иметь тот же тип generi c (или расширенный), что и объект, для которого был вызван метод. В вашей первой реализации метод dsl.selectQuery () возвращает SelectQuery , и обе переменные query и unionQuery имеют одинаковые Тип generi c и метод объединения определены правильно.

Во второй реализации query и аргумент query.union (...) имеют Выберите <*> тип. Я предполагаю, что компилятор считает, что обе переменные имеют разные generi c (поскольку он не определен и может отличаться), и компилятор не может использовать объединение из Select, но обе переменные реализуют Iterable и компилятор выберите Iterable.union , так как он подходит.

2 голосов
/ 27 февраля 2020

Проблема в том, что для Select union параметр типа аргумента должен быть подтипом получателя (? extends R); если оба значения Select<*>, это может быть, например, Select<Int>.union(Select<String>), который не будет проверять тип. Это не происходит для Iterable, потому что это ковариант.

Я думаю, что buildSelect можно набрать более точно как

private fun <T : Record> buildSelect(selectCommonPart: Field<T>, whereCommonPart: Condition, date: LocalDate): Select<T>

, а затем, поскольку оба вызова имеют один и тот же первый аргумент, должно сработать.

РЕДАКТИРОВАТЬ: я просто предположил, что типы selectCommonPart и возвращаемый запрос должен совпадать, но они не и dsl.selectQuery() возвращает Select<Record>, поэтому подпись в ответе Максима Попова верна.

...