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.
package com.kenshoo.pl.entity.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.kenshoo.jooq.*;
import com.kenshoo.pl.data.ImpersonatorTable;
import com.kenshoo.pl.entity.UniqueKey;
import com.kenshoo.pl.entity.*;
import com.kenshoo.pl.entity.internal.fetch.*;
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 static com.kenshoo.pl.entity.Feature.FetchMany;
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.lambda.Seq.seq;
public class EntitiesFetcher {
private final DSLContext dslContext;
private final FeatureSet features;
private final OldEntityFetcher oldEntityFetcher;
public EntitiesFetcher(DSLContext dslContext) {
this(dslContext, FeatureSet.EMPTY);
}
public EntitiesFetcher(DSLContext dslContext, FeatureSet features) {
this.dslContext = dslContext;
this.features = features;
this.oldEntityFetcher = new OldEntityFetcher(dslContext);
}
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 (!features.isEnabled(FetchMany)) {
return oldEntityFetcher.fetchEntitiesByIds(ids, fieldsToFetch);
}
if (ids.isEmpty()) {
return Collections.emptyMap();
}
final UniqueKey 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");
if (!features.isEnabled(FetchMany)) {
return oldEntityFetcher.fetch(entityType, plCondition, fieldsToFetch);
}
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");
if (!features.isEnabled(FetchMany)) {
return oldEntityFetcher.fetch(entityType, keys, plCondition, fieldsToFetch);
}
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, CurrentEntityState> fetchEntitiesByForeignKeys(E entityType, UniqueKey foreignUniqueKey, Collection extends Identifier> keys, Collection> fieldsToFetch) {
if (!features.isEnabled(FetchMany)) {
return oldEntityFetcher.fetchEntitiesByForeignKeys(entityType, foreignUniqueKey, keys, 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) {
if (!features.isEnabled(FetchMany)) {
return oldEntityFetcher.fetchPartialEntities(entityType, ids, 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) {
if (!features.isEnabled(FetchMany)) {
return oldEntityFetcher.fetchByCondition(entityType, condition, 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 , 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