All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.kenshoo.pl.entity.internal.fetch.QueryBuilder Maven / Gradle / Ivy

Go to download

A Java persistence layer based on JOOQ for high performance and business flow support.

There is a newer version: 0.1.121-jooq-3.16.3
Show newest version
package com.kenshoo.pl.entity.internal.fetch;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.kenshoo.jooq.*;
import com.kenshoo.pl.data.ImpersonatorTable;
import com.kenshoo.pl.entity.*;
import com.kenshoo.pl.entity.UniqueKey;
import org.jooq.*;
import org.jooq.impl.DSL;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;

import static org.jooq.lambda.function.Functions.not;

public class QueryBuilder> {

    private DSLContext dslContext;
    private List> selectedFields;
    private DataTable startingTable;
    private List paths;
    private Set oneToOneTableRelations;
    private Collection> ids;


    public QueryBuilder(DSLContext dslContext) {
        this.dslContext = dslContext;
    }

    public QueryBuilder selecting(List> selectedFields) {
        this.selectedFields = selectedFields;
        return this;
    }

    public QueryBuilder from(DataTable primaryTable) {
        this.startingTable = primaryTable;
        return this;
    }

    public QueryBuilder innerJoin(List paths) {
        this.paths = paths;
        return this;
    }

    public QueryBuilder innerJoin(TreeEdge path) {
        this.paths = Lists.newArrayList(path);
        return this;
    }

    public QueryBuilder leftJoin(Set oneToOneTableRelations) {
        this.oneToOneTableRelations = oneToOneTableRelations;
        return this;
    }

    public QueryBuilder whereIdsIn(Collection> ids) {
        this.ids = ids;
        return this;
    }

    public QueryExtension> build() {
        final SelectJoinStep query = dslContext.select(selectedFields).from(startingTable);
        final Set joinedTables = Sets.newHashSet(startingTable);
        paths.forEach(edge -> joinTables(query, joinedTables, edge));
        if (oneToOneTableRelations != null) {
            joinSecondaryTables(query, joinedTables, oneToOneTableRelations);
        }
        if (ids != null) {
            final UniqueKey uniqueKey = ids.iterator().next().getUniqueKey();
            return addIdsCondition(query, startingTable, uniqueKey, ids);
        }
        return new QueryExtension>() {
            @Override
            public SelectJoinStep getQuery() {
                return query;
            }

            @Override
            public void close() {
            }
        };
    }

    public  QueryExtension addIdsCondition(Q query, DataTable primaryTable, UniqueKey uniqueKey, Collection> identifiers) {
        List> conditions = new ArrayList<>();
        for (EntityField field : uniqueKey.getFields()) {
            addToConditions(field, identifiers, conditions);
        }
        primaryTable.getVirtualPartition().forEach(fieldAndValue -> {
            Object[] values = new Object[identifiers.size()];
            Arrays.fill(values, fieldAndValue.getValue());
            conditions.add(new FieldAndValues<>((Field) fieldAndValue.getField(), Arrays.asList(values)));
        });
        return SelectQueryExtender.of(dslContext, query, conditions);
    }

    static void joinTables(SelectJoinStep query, Set alreadyJoinedTables, TreeEdge edgeInThePath) {
        // The joins must be composed in the order of traversal, so we have to "unwind" the path traveled from the root
        // Using a stack for that
        LinkedList joins = new LinkedList<>();
        joins.push(edgeInThePath);
        // Push onto the stack until we reach a table already joined (or the starting table)
        while (!alreadyJoinedTables.contains(edgeInThePath.source.table)) {
            edgeInThePath = edgeInThePath.source.parent;
            joins.push(edgeInThePath);
        }
        // Perform the joins
        for (TreeEdge join : joins) {
            DataTable rhs = join.target.table;
            query.join(rhs).on(getJoinCondition(join.source.table, join.target.table));
            alreadyJoinedTables.add(rhs);
        }
    }

    static void joinSecondaryTables(SelectJoinStep query, Set> alreadyJoinedTables, Set targetOneToOneRelations) {
        targetOneToOneRelations.stream()
                .filter(not(secondaryTableIn(alreadyJoinedTables)))
                .forEach(addLeftJoinTo(query));
    }

    private static Condition getJoinCondition(Table fromTable, Table toTable) {
        List> foreignKeys = toTable.getReferencesTo(fromTable);
        if (foreignKeys.isEmpty()) {
            foreignKeys = fromTable.getReferencesTo(toTable);
        }
        if (foreignKeys.isEmpty()) {
            return null;
        }
        Condition joinCondition = DSL.trueCondition();
        ForeignKey foreignKey = foreignKeys.get(0);
        org.jooq.UniqueKey key = foreignKey.getKey();
        List> otherTableFields = key.getFields();
        for (int i = 0; i < foreignKey.getFields().size(); i++) {
            TableField tableField = foreignKey.getFields().get(i);
            //noinspection unchecked
            joinCondition = joinCondition.and(tableField.eq((TableField) otherTableFields.get(i)));
        }
        return joinCondition;
    }

    private  void addToConditions(EntityField field, Collection> identifiers, List> conditions) {
        EntityFieldDbAdapter dbAdapter = field.getDbAdapter();
        List fieldValues = new ArrayList<>(identifiers.size());
        for (Identifier identifier : identifiers) {
            dbAdapter.getDbValues(identifier.get(field)).sequential().forEach(fieldValues::add);
        }
        Optional> tableField = dbAdapter.getTableFields().findFirst();
        conditions.add(new FieldAndValues<>((TableField) tableField.get(), fieldValues));
    }

    private static Predicate secondaryTableIn(Set> joinedTables) {
        return relation -> joinedTables.contains(relation.getSecondary());
    }

    private static Consumer addLeftJoinTo(SelectJoinStep query) {
        return relation -> query.leftOuterJoin(relation.getSecondary()).on(getJoinCondition(relation));
    }

    private static Condition getJoinCondition(final OneToOneTableRelation relation) {
        return getJoinCondition(relation.getSecondary(), relation.getPrimary());
    }
}