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

com.kenshoo.pl.entity.DeletionCommandPopulator 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;

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> parents, ChangeFlowConfig config) {

        final Collection> 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> 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> 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> 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> 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> 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> 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