Jooq расширить существующий диалект. Принять MySQL диалект к Apache Hive диалект

Я пытаюсь использовать JOOQ для запроса Hive. Диалект Hive SQL довольно близок к диалекту MySQL. Прямо сейчас я встретил эти проблемы:

  • Hive поддерживает LIMIT N, он не поддерживает LIMIT N OFFSET K. Пустое решение - переопределить select.limit(limit);

Каковы лучшие методы решения таких проблем в JOOQ?

2 ответа

Решение

Вот самое грязное решение:) Группа пользователей JOOQ, к сожалению, не ответила:(

public class CountRatingQueryBuilder {

    private static final String SCORING_TABLE_NAME = "web_resource_rating";

    private final Connection connection;
    private final ScoringMetadata scoringMetadata;

    private final SelectSelectStep select;
    private final Factory create;

    public CountRatingQueryBuilder(Connection connection, ScoringMetadata scoringMetadata){
        this.connection = connection;
        this.scoringMetadata = scoringMetadata;

        create = new Factory(this.connection, SQLDialect.MYSQL);
        select = create.select();

        withSelectFieldsClause();
    }

    public CountRatingQueryBuilder withLimit(int limit){
        select.limit(limit);
        return this;
    }

    public CountRatingQueryBuilder withRegionId(Integer regionId){
        select.where(REGION_ID.field().equal(regionId));
        return this;
    }

    public CountRatingQueryBuilder withResourceTypeId(int resourceTypeId){
        select.where(RESOURCE_TYPE_ID.field().equal(resourceTypeId));
        return this;
    }

    public CountRatingQueryBuilder withRequestTimeBetween(long beginTimestamp, long endTimestamp){
        select.where(REQUEST_TIME.field().between(beginTimestamp, endTimestamp));
        return this;
    }

    public CountRatingQueryBuilder withResourceId(int resourceId){
        select.where(RESOURCE_ID.field().equal(resourceId));
        return this;
    }



    protected void withGroupByClause(){
        select.groupBy(REGION_ID.field());
        select.groupBy(RESOURCE_TYPE_ID.field());
        select.groupBy(RESOURCE_ID.field());
        select.groupBy(CONTENT_ID.field());
    }

    protected void withSelectFieldsClause(){
        select.select(REGION_ID.field());
        select.select(RESOURCE_TYPE_ID.field());
        select.select(CONTENT_ID.field());
        select.select(RESOURCE_ID.field());
        select.select(Factory.count(HIT_COUNT.field()).as(SUM_HIT_COUNT.fieldName()));
    }

    protected void withFromClause(){
        select.from(SCORING_TABLE_NAME);
    }

    protected void withOrderByClause(){
        select.orderBy(SUM_HIT_COUNT.field().desc());
    }

    public String build(){
        withGroupByClause();
        withOrderByClause();
        withFromClause();
        return select.getSQL().replace("offset ?","");//dirty hack for MySQL dialect. TODO: we can try to implement our own SQL dialect for Hive :)

    }

    public List<ResultRow> buildAndFetch(){
        String sqlWithPlaceholders = build();

        List<ResultRow> scoringResults = new ArrayList<ResultRow>(100);
        List<Record> recordResults = create.fetch(sqlWithPlaceholders, ArrayUtils.subarray(select.getBindValues().toArray(new Object[select.getBindValues().size()]),0, select.getBindValues().size()-1));//select.fetch();
        for(Record record : recordResults){
            ResultRowBuilder resultRowBuilder = ResultRowBuilder.create();

            resultRowBuilder.withContentType(scoringMetadata.getResourceType(record.getValue(RESOURCE_TYPE_ID.fieldName(), Integer.class)));
            resultRowBuilder.withHitCount(record.getValue(SUM_HIT_COUNT.fieldName(), Long.class));
            resultRowBuilder.withUrl(record.getValue(CONTENT_ID.fieldName(), String.class));
            scoringResults.add(resultRowBuilder.build());
        }
        return scoringResults;
    }

}

К сожалению, расширение jOOQ для полной поддержки нового диалекта SQL не очень просто. Со временем API jOOQ расширился, поддерживая большой набор стандартных и специфичных для поставщика вариантов синтаксиса SQL. Хотя диалект Apache Hive может показаться похожим на MySQL, вероятно, существует множество тонких различий, которые необходимо будет реализовать во внутренних компонентах jOOQ. Различная реализация предложения LIMIT .. OFFSET только одна проблема. Тем не менее, как правило, не очень хорошая идея использовать jOOQ с "неизвестным" или "неподдерживаемым" диалектом.

Решение: в краткосрочной перспективе

В краткосрочной перспективе вам, вероятно, придется исправлять рендеринг SQL в jOOQ. Лучший метод для этого - использовать ExecuteListener, как описано здесь:

Получив событие renderEnd(), вы сможете получить доступ к визуализированному SQL и изменить его, используя регулярные выражения или любой другой метод, который вы предпочитаете.

Решение: в долгосрочной перспективе

В долгосрочной перспективе может быть лучшее решение, если / когда #2337 будет реализован (но мы, вероятно, не будем это реализовывать)

Другие вопросы по тегам