com.kenshoo.pl.entity.DeletionCommandPopulator 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;
import com.google.common.annotations.VisibleForTesting;
import com.kenshoo.pl.entity.internal.ChildrenIdFetcher;
import org.jooq.DSLContext;
import java.util.*;
import java.util.stream.Stream;
import static com.kenshoo.pl.entity.UniqueKeyValue.concat;
import static java.util.Collections.emptyMap;
import static java.util.stream.Collectors.*;
import static org.jooq.lambda.Seq.seq;
public class DeletionCommandPopulator {
private final ChildrenIdFetcher childrenIdFetcher;
public DeletionCommandPopulator(PLContext plContext) {
childrenIdFetcher = new ChildrenIdFetcher(plContext);
}
@VisibleForTesting
public DeletionCommandPopulator(ChildrenIdFetcher childrenIdFetcher) {
this.childrenIdFetcher = childrenIdFetcher;
}
public >
void handleRecursive(Iterable extends ChangeEntityCommand> parents, ChangeFlowConfig config) {
final Collection extends ChangeEntityCommand> parentsWithChildSupplier = seq(parents)
.filter(this::recursivelyCheckIfAnyShouldCascade)
.toList();
if (parentsWithChildSupplier.isEmpty()) {
return;
}
seq(config.childFlows()).forEach(childflow -> handleChildFlow(parentsWithChildSupplier, childflow));
}
private , CHILD extends EntityType>
void handleChildFlow(Collection extends ChangeEntityCommand> parents, ChangeFlowConfig childFlow) {
final CHILD childType = childFlow.getEntityType();
ChildrenFromDB childrenFromDB = getExistingChildrenFromDB(parents, childType);
addDeletionChildCommands(seq(parents).filter(this::isCascadeDeletion), childType, childrenFromDB);
supplyChildCommands(seq(parents).filter(this::withMissingChildSupplier), childType, childrenFromDB);
populateKeyToParent(parents, childType, childrenFromDB);
handleRecursive(seq(parents).flatMap(p -> p.getChildren(childType)).filter(child -> child.getKeysToParent() != null), childFlow);
}
private , CHILD extends EntityType>
ChildrenFromDB getExistingChildrenFromDB(Collection extends ChangeEntityCommand> parents, CHILD childType) {
List> parentIds = seq(parents).map(p -> concatenatedId(p)).toList();
final IdentifierType childKey = identifierOfFirstChildCmd(parents, childType).orElseGet(childType::getPrimaryKey);
return new ChildrenFromDB<>(childrenIdFetcher.fetch(parentIds, childKey));
}
private , CHILD extends EntityType>
void supplyChildCommands(Stream extends ChangeEntityCommand> parents, CHILD childType, ChildrenFromDB childrenFromDB) {
parents.forEach(parent -> {
final Set> childrenFromCommand = childrenIdsOf(childType, parent);
seq(childrenFromDB.getChildIds(parent))
.filter(childIds -> !childrenFromCommand.contains(childIds))
.forEach(missingChildIds -> {
parent.getMissingChildrenSupplier(childType).flatMap(s -> s.supplyNewCommand(missingChildIds)).ifPresent(newCmd -> {
parent.addChild(newCmd);
});
});
});
}
private , CHILD extends EntityType>
void addDeletionChildCommands(Stream extends ChangeEntityCommand> parents, CHILD childType, ChildrenFromDB childrenFromDB) {
parents.forEach(parent ->
childrenFromDB.getChildIds(parent)
.forEach(childId -> {
DeleteEntityCommand> newCmd = new DeleteEntityCommand<>(childType, childId).setCascade();
parent.addChild(newCmd);
})
);
}
private , CHILD extends EntityType>
void populateKeyToParent(Collection extends ChangeEntityCommand> parents, CHILD childType, ChildrenFromDB childrenFromDB) {
seq(parents).forEach(parent -> {
ChildrenWithKeyToParent childrenOfParent = childrenFromDB.of(parent);
parent.getChildren(childType).forEach(child -> child.setKeysToParent(childrenOfParent.keyToParentOf(child.getIdentifier())));
});
}
private static > Identifier concatenatedId(ChangeEntityCommand currentState) {
return concat(currentState.getIdentifier(), currentState.getKeysToParent());
}
private , CHILD extends EntityType>
Set> childrenIdsOf(CHILD childType, ChangeEntityCommand parent) {
return parent.getChildren(childType).map(EntityChange::getIdentifier)
.filter(Objects::nonNull)
.collect(toSet());
}
private >
boolean recursivelyCheckIfAnyShouldCascade(ChangeEntityCommand parent) {
return withMissingChildSupplier(parent) || isCascadeDeletion(parent) || parent.getChildren().anyMatch(child -> recursivelyCheckIfAnyShouldCascade(child));
}
private >
boolean isCascadeDeletion(ChangeEntityCommand cmd) {
return cmd instanceof DeleteEntityCommand && ((DeleteEntityCommand) cmd).isCascade();
}
private > boolean withMissingChildSupplier(ChangeEntityCommand cmd) {
return !cmd.getMissingChildrenSuppliers().isEmpty();
}
private , CHILD extends EntityType>
Optional>
identifierOfFirstChildCmd(Collection extends ChangeEntityCommand> parents, CHILD childType) {
return parents.stream()
.flatMap(p -> p.getChildren(childType))
.map(EntityChange::getIdentifier)
.filter(Objects::nonNull)
.map(Identifier::getUniqueKey)
.findFirst();
}
/**
* Lookup maps for everything we fetched from DB.
* Parent identifiers are concatenated IDs (see method concatenatedId) so they are always unique.
*/
private static class ChildrenFromDB, CHILD extends EntityType> {
final Map, ChildrenWithKeyToParent> map;
final ChildrenWithKeyToParent EMPTY = new ChildrenWithKeyToParent<>(emptyMap());
public ChildrenFromDB(Stream> stream) {
map = stream.collect(groupingBy(FullIdentifier::getParentId,
collectingAndThen(toMap(FullIdentifier::getChildId, FullIdentifier::getKetToParent), ChildrenWithKeyToParent::new)));
}
ChildrenWithKeyToParent of(ChangeEntityCommand parent) {
return map.getOrDefault(concatenatedId(parent), EMPTY);
}
Set> getChildIds(ChangeEntityCommand parent) {
return of(parent).map.keySet();
}
}
/**
* This is a subset of what we fetched from DB: these are the children of
* a specific parent.
*/
private static class ChildrenWithKeyToParent> {
// child identifier (from the original command) mapped to the child keyToParent.
// this is required to populate keyToParent for the child commands to be available
// for the next recursive iteration.
final Map, Identifier> map;
private ChildrenWithKeyToParent(Map, Identifier> childrenWithKeyToParent) {
this.map = childrenWithKeyToParent;
}
public Identifier keyToParentOf(Identifier childId) {
return map.get(childId);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy