com.kenshoo.pl.entity.internal.EntitiesFetcher Maven / Gradle / Ivy
                 Go to download
                
        
                    Show more of this group  Show more artifacts with this name
Show all versions of persistence-layer Show documentation
                Show all versions of persistence-layer Show documentation
A Java persistence layer based on JOOQ for high performance and business flow support.
                
             The newest version!
        
        package com.kenshoo.pl.entity.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.kenshoo.jooq.DataTable;
import com.kenshoo.jooq.QueryExtension;
import com.kenshoo.jooq.TempTableHelper;
import com.kenshoo.jooq.TempTableResource;
import com.kenshoo.pl.data.ImpersonatorTable;
import com.kenshoo.pl.entity.UniqueKey;
import com.kenshoo.pl.entity.*;
import com.kenshoo.pl.entity.internal.fetch.AliasedKey;
import com.kenshoo.pl.entity.internal.fetch.ExecutionPlan;
import com.kenshoo.pl.entity.internal.fetch.QueryBuilder;
import com.kenshoo.pl.entity.internal.fetch.RecordReader;
import org.jooq.*;
import org.jooq.lambda.Seq;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang3.Validate.notEmpty;
import static org.jooq.impl.DSL.count;
import static org.jooq.lambda.Seq.seq;
public class EntitiesFetcher {
    private final DSLContext dslContext;
    private final FeatureSet features;
    public EntitiesFetcher(DSLContext dslContext) {
        this(dslContext, FeatureSet.EMPTY);
    }
    public EntitiesFetcher(DSLContext dslContext, FeatureSet features) {
        this.dslContext = dslContext;
        this.features = features;
    }
    public > Map, CurrentEntityState> fetchEntitiesByIds(final Collection extends Identifier> ids,
                                                                                               final EntityField, ?>... fieldsToFetchArgs) {
        return fetchEntitiesByIds(ids, ImmutableList.copyOf(fieldsToFetchArgs));
    }
    public > Map, CurrentEntityState> fetchEntitiesByIds(final Collection extends Identifier> ids,
                                                                                               final Collection extends EntityField, ?>> fieldsToFetch) {
        if (ids.isEmpty()) {
            return Collections.emptyMap();
        }
        final IdentifierType uniqueKey = ids.iterator().next().getUniqueKey();
        final AliasedKey aliasedKey = new AliasedKey<>(uniqueKey);
        return fetchEntities(uniqueKey.getEntityType().getPrimaryTable(), aliasedKey, fieldsToFetch, query -> query.whereIdsIn(ids));
    }
    public > List fetch(final EntityType entityType,
                                                                    final PLCondition plCondition,
                                                                    final EntityField, ?>... fieldsToFetch) {
        requireNonNull(entityType, "An entity type must be provided");
        requireNonNull(plCondition, "A condition must be provided");
        notEmpty(fieldsToFetch, "There must be at least one field to fetch");
        final AliasedKey aliasedKey = new AliasedKey<>(entityType.getPrimaryKey());
        final List extends EntityField, ?>> allFieldsToFetch = Seq.concat(Seq.of(fieldsToFetch), seq(plCondition.getFields())).distinct().collect(Collectors.toList());
        return Lists.newArrayList(fetchEntities(entityType.getPrimaryTable(), aliasedKey, allFieldsToFetch, query -> query.withCondition(plCondition.getJooqCondition())).values());
    }
    public List fetch(final EntityType> entityType,
                                          final Collection extends Identifier>> keys,
                                          final PLCondition plCondition,
                                          final EntityField, ?>... fieldsToFetch) {
        requireNonNull(entityType, "An entity type must be provided");
        notEmpty(keys, "There must be at least one keys to fetch");
        requireNonNull(plCondition, "A condition must be provided");
        notEmpty(fieldsToFetch, "There must be at least one field to fetch");
        final AliasedKey> aliasedKey = new AliasedKey<>(entityType.getPrimaryKey());
        final List extends EntityField, ?>> allFieldsToFetch = Seq.concat(Seq.of(fieldsToFetch), seq(plCondition.getFields()), fieldsOf(keys)).distinct().collect(Collectors.toList());
        return Lists.newArrayList(fetchEntities(entityType.getPrimaryTable(), aliasedKey, allFieldsToFetch, query -> query.whereIdsIn(keys).withCondition(plCondition.getJooqCondition())).values());
    }
    public > Map, Integer> fetchCount(final E entityType,
                                                                            final Collection extends Identifier>> groupingIdentifiers,
                                                                            final PLCondition plCondition) {
        requireNonNull(entityType, "An entity type must be provided");
        notEmpty(groupingIdentifiers, "There must be at least one field in the groupingFields");
        requireNonNull(plCondition, "A condition must be provided");
        final var groupKey = groupingIdentifiers.iterator().next().getUniqueKey();
        final var aliasedGroupKey = new AliasedKey<>(groupKey);
        final var fieldsForExecutionPlan = Seq.concat(seq(plCondition.getFields()), Stream.of(groupKey.getFields())).distinct().toList();
        final var executionPlan = new ExecutionPlan(entityType.getPrimaryTable(), fieldsForExecutionPlan).getOneToOnePlan();
        final Field countField = count();
        final var queryBuilder = new QueryBuilder(dslContext)
                .selecting(Seq.>seq(aliasedGroupKey.aliasedFields()).append(countField).toList())
                .from(entityType.getPrimaryTable())
                .innerJoin(executionPlan.getPaths())
                .leftJoin(executionPlan.getSecondaryTableRelations())
                .whereIdsIn(groupingIdentifiers)
                .withCondition(plCondition.getJooqCondition())
                .with(q -> q.groupBy(groupKey.getTableFields()));
        try (var queryExtender = queryBuilder.build()) {
            return fetchCount(queryExtender.getQuery(), aliasedGroupKey, countField);
        }
    }
    public > Map, CurrentEntityState> fetchEntitiesByForeignKeys(E entityType, UniqueKey foreignUniqueKey, Collection extends Identifier> keys, Collection> fieldsToFetch) {
        try (final TempTableResource foreignKeysTable = createForeignKeysTable(entityType.getPrimaryTable(), foreignUniqueKey, keys)) {
            final AliasedKey aliasedKey = new AliasedKey<>(foreignUniqueKey, foreignKeysTable);
            final DataTable startingTable = foreignKeysTable.getTable();
            return fetchEntities(startingTable, aliasedKey, fieldsToFetch, QueryBuilder::withoutPartitions);
        }
    }
    public , PE extends PartialEntity, ID extends Identifier> Map fetchPartialEntities(E entityType, Collection ids, final Class entityIface) {
        final Map> entityMethodsMap = EntityTypeReflectionUtil.getMethodsMap(entityType, entityIface);
        Map, CurrentEntityState> entitiesMap = fetchEntitiesByIds(ids, entityMethodsMap.values());
        return seq(ids).filter(entitiesMap::containsKey).collect(toMap(identity(), id -> createInstance(entityIface, entityMethodsMap, entitiesMap.get(id))));
    }
    public , PE extends PartialEntity> List fetchByCondition(E entityType, final Condition condition, final Class entityIface) {
        final Map> entityMethodsMap = EntityTypeReflectionUtil.getMethodsMap(entityType, entityIface);
        final AliasedKey> aliasedKey = new AliasedKey<>(entityType.getPrimaryKey());
        Collection entities = fetchEntities(entityType.getPrimaryTable(), aliasedKey, entityMethodsMap.values(), query -> query.withCondition(condition)).values();
        return entities.stream()
                .map(entity -> createInstance(entityIface, entityMethodsMap, entity))
                .collect(toList());
    }
    private > Map, CurrentEntityState> fetchEntities(
            final DataTable startingTable,
            final AliasedKey aliasedKey,
            final Collection extends EntityField, ?>> fieldsToFetch,
            Consumer> queryModifier
    ) {
        final ExecutionPlan executionPlan = new ExecutionPlan(startingTable, fieldsToFetch);
        final ExecutionPlan.OneToOnePlan oneToOnePlan = executionPlan.getOneToOnePlan();
        final QueryBuilder mainQueryBuilder = new QueryBuilder(dslContext).selecting(selectFieldsOf(oneToOnePlan.getFields(), aliasedKey))
                .from(startingTable)
                .innerJoin(oneToOnePlan.getPaths())
                .leftJoin(oneToOnePlan.getSecondaryTableRelations());
        queryModifier.accept(mainQueryBuilder);
        final Map, CurrentEntityState> entities = fetchMainEntities(aliasedKey, oneToOnePlan, mainQueryBuilder);
        executionPlan.getManyToOnePlans().forEach(plan -> {
            final QueryBuilder subQueryBuilder = new QueryBuilder(dslContext).selecting(selectFieldsOf(plan.getFields(), aliasedKey))
                    .from(startingTable)
                    .innerJoin(plan.getPath());
            queryModifier.accept(subQueryBuilder);
            fetchAndPopulateSubEntities(aliasedKey, entities, plan, subQueryBuilder);
        });
        return entities;
    }
    private , SUB extends EntityType> void fetchAndPopulateSubEntities(AliasedKey aliasedKey, Map, CurrentEntityState> entities, ExecutionPlan.ManyToOnePlan plan, QueryBuilder queryBuilder) {
        try (QueryExtension> queryExtender = queryBuilder.build()) {
            Map, List>> multiValuesMap = fetchMultiValuesMap(queryExtender.getQuery(), aliasedKey, plan.getFields());
            multiValuesMap.forEach((Identifier id, List> multiValues) -> {
                final SUB subEntityType = entityTypeOf(plan.getFields());
                ((CurrentEntityMutableState) entities.get(id)).add(subEntityType, multiValues);
            });
        }
    }
    private > Map, CurrentEntityState> fetchMainEntities(AliasedKey aliasedKey, ExecutionPlan.OneToOnePlan oneToOnePlan, QueryBuilder queryBuilder) {
        try (QueryExtension> queryExtender = queryBuilder.build()) {
            return fetchEntitiesMap(queryExtender.getQuery(), aliasedKey, oneToOnePlan.getFields());
        }
    }
    private > Map, CurrentEntityState> fetchEntitiesMap(ResultQuery query, AliasedKey aliasedKey, List extends EntityField, ?>> fields) {
        return query.fetchMap(record -> RecordReader.createKey(record, aliasedKey), record -> RecordReader.createEntity(record, fields));
    }
    private > Map, Integer> fetchCount(ResultQuery query, AliasedKey> key, Field count) {
        return query.fetchMap(record -> RecordReader.createKey(record, key), record -> record.get(count));
    }
    private , SUB extends EntityType> Map, List>> fetchMultiValuesMap(ResultQuery query, AliasedKey aliasedKey, List extends EntityField> fields) {
        final Map, List>> multiValuesMap = new HashMap<>();
        query.fetchInto(record -> {
            Identifier key = RecordReader.createKey(record, aliasedKey);
            multiValuesMap.computeIfAbsent(key, k -> Lists.newArrayList());
            multiValuesMap.get(key).add(RecordReader.createFieldsValueMap(record, fields));
        });
        return multiValuesMap;
    }
    public > TempTableResource createForeignKeysTable(final DataTable primaryTable, final UniqueKey foreignUniqueKey, final Collection extends Identifier> keys) {
        ImpersonatorTable impersonatorTable = new ImpersonatorTable(primaryTable);
        foreignUniqueKey.getTableFields().forEach(impersonatorTable::createField);
        return TempTableHelper.tempInMemoryTable(dslContext, impersonatorTable, batchBindStep -> {
                    for (Identifier key : keys) {
                        EntityField[] keyFields = foreignUniqueKey.getFields();
                        List                                                                                      © 2015 - 2025 Weber Informatics LLC | Privacy Policy