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

org.nuiton.topia.templates.sql.EntitySqlDescriptorGenerator Maven / Gradle / Ivy

There is a newer version: 10.0.1
Show newest version
package org.nuiton.topia.templates.sql;

/*-
 * #%L
 * Toolkit :: Templates
 * %%
 * Copyright (C) 2017 - 2024 Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.GsonBuilder;
import fr.ird.observe.toolkit.templates.services.GenerateDifferentialMetaModelFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.plexus.component.annotations.Component;
import org.nuiton.eugene.Template;
import org.nuiton.eugene.models.object.ObjectModel;
import org.nuiton.eugene.models.object.ObjectModelAttribute;
import org.nuiton.eugene.models.object.ObjectModelClass;
import org.nuiton.eugene.models.object.ObjectModelClassifier;
import org.nuiton.eugene.models.object.ObjectModelType;
import org.nuiton.topia.persistence.TopiaEntity;
import org.nuiton.topia.service.sql.TopiaEntitySqlModelResource;
import org.nuiton.topia.service.sql.blob.TopiaEntitySqlBlob;
import org.nuiton.topia.service.sql.blob.TopiaEntitySqlBlobModel;
import org.nuiton.topia.service.sql.internal.TopiaEntitySqlModelResourceImpl;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataAssociation;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataComposition;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataEntity;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataEntityPath;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataLink;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModelPaths;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataOneToOneComposition;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataReverseAssociation;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataSimpleAssociation;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlAssociationTable;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlDescriptor;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlDescriptors;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlModel;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlReverseAssociationTable;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlReverseCompositionTable;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlSelector;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlSimpleAssociationTable;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlTable;
import org.nuiton.topia.service.sql.plan.copy.TopiaEntitySqlCopyPlan;
import org.nuiton.topia.service.sql.plan.copy.TopiaEntitySqlCopyPlanModel;
import org.nuiton.topia.service.sql.plan.copy.TopiaEntitySqlCopyPlanTask;
import org.nuiton.topia.service.sql.plan.delete.TopiaEntitySqlDeletePlan;
import org.nuiton.topia.service.sql.plan.delete.TopiaEntitySqlDeletePlanModel;
import org.nuiton.topia.service.sql.plan.replicate.TopiaEntitySqlReplicatePlan;
import org.nuiton.topia.service.sql.plan.replicate.TopiaEntitySqlReplicatePlanModel;
import org.nuiton.topia.templates.TopiaTemplateHelper;
import org.nuiton.topia.templates.sql.order.ReplicationOrderBuilderWithEntryPoint;
import org.nuiton.topia.templates.sql.order.ReplicationOrderBuilderWithType;
import org.nuiton.topia.templates.sql.plan.TopiaEntitySqlCopyPlanBuilder;
import org.nuiton.topia.templates.sql.plan.TopiaEntitySqlCopyPlanBuilderForEntryPoint;
import org.nuiton.topia.templates.sql.plan.TopiaEntitySqlCopyPlanBuilderStandalone;
import org.nuiton.topia.templates.sql.plan.TopiaEntitySqlDeletePlanBuilderForEntryPoint;
import org.nuiton.topia.templates.sql.plan.TopiaEntitySqlDeletePlanBuilderForType;
import org.nuiton.topia.templates.sql.plan.TopiaEntitySqlReplicatePlanBuilder;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;




/**
 * Created on 22/09/2020.
 *
 * @author Tony Chemit - [email protected]
 * @since 1.27
 */
@Component(role = Template.class, hint = "org.nuiton.topia.templates.sql.EntitySqlDescriptorGenerator")
public class EntitySqlDescriptorGenerator extends TopiaMetadataModelGeneratorSupport {

    private static final String SQL_FROM = "%1$s.%2$s %2$s";
    private static final String SQL_INNER_JOIN = "INNER JOIN %1$s.%2$s %2$s ON %2$s.%3$s = %4$s.%5$s";
    private static final String SQL_WHERE_CLAUSE_ALIAS = "%1$s.%2$s";
    private static final Logger log = LogManager.getLogger(EntitySqlDescriptorGenerator.class);
    private final ArrayListMultimap selectors = ArrayListMultimap.create();
    private final Map metadataToEntityMapping = new LinkedHashMap<>();
    private final Map usages = new TreeMap<>();
    private final Map blobs = new TreeMap<>();
    private final ArrayListMultimap reverseAssociations = ArrayListMultimap.create();
    private final ArrayListMultimap reverseCompositions = ArrayListMultimap.create();
    private final ArrayListMultimap mandatoryReverseCompositions = ArrayListMultimap.create();
    private final Map> replicationOrderByType = new TreeMap<>();
    private final Set entryPoints = new TreeSet<>();
    private final Map descriptors = new TreeMap<>();
    protected final GsonBuilder gson = TopiaEntitySqlModelResourceImpl.getGsonBuilder();
    protected TopiaEntitySqlCopyPlanModel modelCopyPlan;
    protected Map gavToLiteral;
    protected Set standaloneEntities;
    protected List replicationOrderWithStandalone;
    protected TopiaEntitySqlReplicatePlanModel modelReplicatePlan;
    protected TopiaEntitySqlDeletePlanModel modelDeletePlan;
    protected TopiaEntitySqlModel sqlModel;
    protected Map fqnToLiteral;
    protected Set standaloneLiterals;
    protected Multimap reverseAssociations2;
    protected String step;

    @Override
    public void applyTemplate(ObjectModel model, File destDir) throws IOException {
        this.model = model;
        prepare();
        step = Step.model.name();
        applyTemplate0(model, destDir);
        step = Step.copyPlan.name();
        applyTemplate0(model, destDir);
        step = Step.replicatePlan.name();
        applyTemplate0(model, destDir);
        step = Step.deletePlan.name();
        applyTemplate0(model, destDir);
        step = Step.usage.name();
        applyTemplate0(model, destDir);
        step = Step.blob.name();
        applyTemplate0(model, destDir);
    }

    @Override
    public String getFilenameForModel(ObjectModel model) {
        switch (Objects.requireNonNull(step)) {
            case "model":
                return TopiaEntitySqlModelResourceImpl.toModelLocation(getFilenameForModel0(model));
            case "copyPlan":
                return TopiaEntitySqlModelResourceImpl.toCopyPlanLocation(getFilenameForModel0(model));
            case "replicatePlan":
                return TopiaEntitySqlModelResourceImpl.toReplicatePlanLocation(getFilenameForModel0(model));
            case "deletePlan":
                return TopiaEntitySqlModelResourceImpl.toDeletePlanLocation(getFilenameForModel0(model));
            case "usage":
                return TopiaEntitySqlModelResourceImpl.toUsageModelLocation(getFilenameForModel0(model));
            case "blob":
                return TopiaEntitySqlModelResourceImpl.toBlobModelLocation(getFilenameForModel0(model));
        }
        throw new IllegalStateException("Can't manage step: " + step);
    }

    @Override
    protected void generateFromElement(Object element, File destDir, String filename, ObjectModelType type) {
        if (ObjectModelType.OBJECT_MODEL != type) {
            // only generate on model
            return;
        }
        super.generateFromElement(element, destDir, filename, type);
    }

    @Override
    protected File getDestinationFile(File destDir, String filename) {
        return destDir.toPath().resolve(filename).toFile();
    }

    @Override
    public void generateFromModel(Writer output, ObjectModel input) {
        switch (step) {
            case "model":
                gson.create().toJson(sqlModel, output);
                break;
            case "copyPlan":
                gson.create().toJson(modelCopyPlan, output);
                break;
            case "replicatePlan":
                gson.create().toJson(modelReplicatePlan, output);
                break;
            case "deletePlan":
                gson.create().toJson(modelDeletePlan, output);
                break;
            case "usage":
                gson.create().toJson(Map.of("mapping", usages), output);
                break;
            case "blob":
                gson.create().toJson(new TopiaEntitySqlBlobModel(blobs), output);
                break;
        }
    }

    protected void prepare() {
        TopiaTemplateHelper templateHelper = getTemplateHelper();
        metadataModel = TopiaMetadataModelBuilder.build(isVerbose(), model, templateHelper);
        metadataModel.applyInheritance();
        allPaths = getAllPaths();

        standaloneEntities = metadataModel.streamWithStandalone().collect(Collectors.toSet());
        standaloneLiterals = standaloneEntities.stream().map(TopiaMetadataEntity::getType).collect(Collectors.toSet());
        reverseAssociations2 = ArrayListMultimap.create();
        List entityClasses = templateHelper.getEntityClasses(model, true);
        fqnToLiteral = new TreeMap<>();
        gavToLiteral = new TreeMap<>();
        for (ObjectModelClass entityClass : entityClasses) {
            if (entityClass.isAbstract()) {
                continue;
            }
            String literalName = templateHelper.getEntityEnumLiteralName(entityClass);
            fqnToLiteral.put(entityClass.getQualifiedName(), literalName);
            TopiaMetadataEntity optionalClazz = metadataModel.getEntity(literalName);
            String dbSchemaName = optionalClazz.getDbSchemaName();
            gavToLiteral.put(optionalClazz.getSchemaAndTableName(), literalName);
            metadataToEntityMapping.put(optionalClazz, entityClass);
            Map dbManyToManyAssociationsTableName = optionalClazz.getDbManyToManyAssociationsTableName();
            if (dbManyToManyAssociationsTableName != null) {
                for (Map.Entry entry : dbManyToManyAssociationsTableName.entrySet()) {
                    String key = entry.getKey();
                    String type = optionalClazz.getBdManyToManyAssociationTableName(key);
                    String tableName = entry.getValue();
                    gavToLiteral.put(dbSchemaName + "." + tableName, literalName + "::" + type);
                }
            }
            Map dbManyAssociationsTableName = optionalClazz.getDbManyAssociationsTableName();
            if (dbManyAssociationsTableName != null) {
                for (Map.Entry entry : dbManyAssociationsTableName.entrySet()) {
                    String key = entry.getKey();
                    String type = optionalClazz.getBdManyAssociationTableName(key);
                    String tableName = entry.getValue();
                    gavToLiteral.put(dbSchemaName + "." + tableName, literalName + "::" + type);
                }
            }
            Set reverseAssociations = metadataModel.getReverseAssociations(optionalClazz);
            if (reverseAssociations != null && reverseAssociations.size() > 0) {
                for (TopiaMetadataReverseAssociation reverseAssociation : reverseAssociations) {
                    TopiaMetadataEntity target = reverseAssociation.getTarget();
                    this.reverseAssociations2.put(target, reverseAssociation);
                }
            }
        }
        for (ObjectModelClass entityClass : entityClasses) {
            if (entityClass.isAbstract()) {
                continue;
            }
            generateUsageAndBlob(entityClass);
        }
        metadataModel.streamWithoutAbstract()/*.filter(e -> !standaloneEntities.contains(e))*/.forEach(type -> {
            boolean standalone = standaloneEntities.contains(type);
            boolean entryPoint = type.isEntryPoint();
            List entities;
            TopiaMetadataModelPaths entityPaths;
            if (entryPoint) {
                entityPaths = TopiaMetadataEntityPathsBuilder.forEntryPoint(allPaths, type);
                entities = ReplicationOrderBuilderWithEntryPoint.build(metadataModel, entityPaths, type);
            } else {
                entityPaths = TopiaMetadataEntityPathsBuilder.forType(allPaths, type);
                entities = ReplicationOrderBuilderWithType.build(metadataModel, type, entityPaths);
                if (!standalone) {
                    Set associationsSet = metadataModel.getReverseAssociations(type);
                    for (TopiaMetadataReverseAssociation link : associationsSet) {
                        reverseAssociations.put(link.getTarget().getFullyQualifiedName(), link);
                    }
                    Set compositionsSet = metadataModel.getReverseCompositions(type);
                    if (!compositionsSet.isEmpty()) {
                        for (TopiaMetadataComposition link : compositionsSet) {
                            TopiaMetadataEntity owner = link.getOwner();
                            ObjectModelClassifier classifier = model.getClassifier(owner.getFullyQualifiedName());
                            String dbName = link.getTargetDbName();
                            ObjectModelAttribute attribute = classifier.getAttribute(dbName);
                            Boolean notNullTagValue = templateHelper.topiaHibernateTagValues().getNotNullTagValue(attribute);
                            if (notNullTagValue != null && notNullTagValue) {
                                mandatoryReverseCompositions.put(link.getTarget().getFullyQualifiedName(), link);
                            } else {
                                reverseCompositions.put(link.getTarget().getFullyQualifiedName(), link);
                            }
                        }
                    }
                }
            }
            List entitiesNames = TopiaMetadataEntity.toFqn(entities);
            String entityName = type.getFullyQualifiedName();
            replicationOrderByType.put(entityName, entitiesNames);
            if (entryPoint) {
                entryPoints.add(entityName);
            }
        });

        replicationOrderWithStandalone = GenerateDifferentialMetaModelFile.loadReplicationOrderWithStandalone(getClassLoader(), model.getName());

        // second round to compute TopiaEntitySqlDescriptor
        metadataModel.streamWithoutAbstract().forEach(type -> descriptors.put(type.getFullyQualifiedName(), createDescriptor(type)));

        // third round to compute plans
        Map replicatePlans = new TreeMap<>();
        Map entryPointCopyPlans = new TreeMap<>();
        Map typeDeletePlans = new TreeMap<>();
        metadataModel.streamWithoutAbstract()/*.filter(e -> !standaloneEntities.contains(e))*/.forEach(type -> {
            String fullyQualifiedName = type.getFullyQualifiedName();
            TopiaEntitySqlDescriptor descriptor = descriptors.get(fullyQualifiedName);
            TopiaEntitySqlDeletePlan deletePlan = createDeletePlan(type, descriptor);
            typeDeletePlans.put(fullyQualifiedName, deletePlan);
            boolean entryPoint = entryPoints.contains(fullyQualifiedName);
            if (entryPoint) {
                TopiaEntitySqlCopyPlan copyPlan = createCopyPlan(type);
                entryPointCopyPlans.put(fullyQualifiedName, copyPlan);
            } else {
                TopiaEntitySqlReplicatePlan plan = createReplicatePlan(type, descriptor);
                replicatePlans.put(fullyQualifiedName, plan);
            }
        });
        //DELETE FROM ps_logbook.wellActivitySpecies WHERE wellActivitySpecies.topiaId IN (SELECT wellActivitySpecies.topiaId FROM ps_logbook.wellActivitySpecies wellActivitySpecies INNER JOIN ps_logbook.wellActivity wellActivity ON wellActivity.topiaId = wellActivitySpecies.wellActivity  INNER JOIN ps_logbook.well well ON well.topiaId = wellActivity.well WHERE activity.topiaId %s);
        TopiaEntitySqlCopyPlan standaloneCopyPlan = createStandaloneCopyPlan();
        modelCopyPlan = new TopiaEntitySqlCopyPlanModel(entryPointCopyPlans, standaloneCopyPlan);
        modelReplicatePlan = new TopiaEntitySqlReplicatePlanModel(replicatePlans);
        modelDeletePlan = new TopiaEntitySqlDeletePlanModel(typeDeletePlans);
        Set schemaNames = new TreeSet<>();
        for (TopiaEntitySqlDescriptor entitySqlDescriptor : descriptors.values()) {
            String schema = entitySqlDescriptor.getTable().getSchemaName();
            schemaNames.add(schema);
        }
        sqlModel = new TopiaEntitySqlModel(List.copyOf(schemaNames), replicationOrderWithStandalone, descriptors);
    }

    protected void applyTemplate0(ObjectModel model, File destDir) {
        File realTarget = TopiaMetadataModelGeneratorSupport.getNotGeneratedResourceDirector(destDir);
        String filename = getFilenameForModel(model);
        generateFromElement(model, realTarget, filename, ObjectModelType.OBJECT_MODEL);
    }

    protected String getFilenameForModel0(ObjectModel model) {
        return super.getFilenameForModel(model);
    }

    protected TopiaEntitySqlDescriptor createDescriptor(TopiaMetadataEntity entity) {
        Collection paths = allPaths.getEntityPath(entity);
        boolean noPath = paths == null || paths.isEmpty();
        TopiaEntitySqlTable table = generateTable(entity, paths);
        List associations = generateAssociations(entity, noPath);
        List simpleAssociations = generateSimpleAssociations(entity, noPath);
        List reverseAssociationTables = generateReverseAssociations(entity, noPath);
        List reverseCompositionTables = generateReverseCompositions(entity, noPath);
        List replicationOrder = replicationOrderByType.get(entity.getFullyQualifiedName());
        return new TopiaEntitySqlDescriptor(table, associations, simpleAssociations, reverseAssociationTables, reverseCompositionTables, replicationOrder, noPath);
    }

    protected TopiaEntitySqlDeletePlan createDeletePlan(TopiaMetadataEntity entity, TopiaEntitySqlDescriptor descriptor) {
        String type = entity.getFullyQualifiedName();
        boolean entryPoint = entryPoints.contains(type);
        List replicationOrder = replicationOrderByType.get(type);
        if (entryPoint) {
            TopiaEntitySqlDescriptors replicationOrderDescriptors = getDescriptors(replicationOrder).reverse();
            TopiaEntitySqlDeletePlanBuilderForEntryPoint builder = new TopiaEntitySqlDeletePlanBuilderForEntryPoint(replicationOrderDescriptors);
            return builder.build();
        }
        TopiaEntitySqlDescriptors replicationOrderDescriptors = getDescriptors(replicationOrder).reverse();
        Multimap extraTableToDelete = ArrayListMultimap.create();
        if (mandatoryReverseCompositions.containsKey(type)) {
            addExtraTableToDelete(descriptor, extraTableToDelete);
        } else {
            // try on shell to replicate
            for (TopiaEntitySqlDescriptor toReplicateDescriptor : replicationOrderDescriptors) {
                String entityName = toReplicateDescriptor.getTable().getEntityName();
                if (mandatoryReverseCompositions.containsKey(entityName)) {
                    addExtraTableToDelete(toReplicateDescriptor, extraTableToDelete);
                }
            }
        }
        TopiaEntitySqlDeletePlanBuilderForType builder = new TopiaEntitySqlDeletePlanBuilderForType(replicationOrderDescriptors, descriptor, extraTableToDelete);
        return builder.build();
    }

    private void addExtraTableToDelete(TopiaEntitySqlDescriptor toReplicateDescriptor, Multimap extraTableToDelete) {
        if (toReplicateDescriptor.getReverseCompositionTables().isEmpty()) {
            return;
        }
        for (TopiaEntitySqlReverseCompositionTable reverseAssociationTable : Objects.requireNonNull(toReplicateDescriptor.getReverseCompositionTables().get())) {
            String entityName = reverseAssociationTable.getEntityName();
            TopiaEntitySqlDescriptor sqlDescriptor = descriptors.get(entityName);
            TopiaEntitySqlTable table = sqlDescriptor.getTable();
            List selectors = table.getSelectors();
            if (sqlDescriptor.getReplicationOrder().size() > 1) {
                // this means we need also to process
                for (String s : sqlDescriptor.getReplicationOrder()) {
                    if (!s.equals(entityName)) {
                        TopiaEntitySqlDescriptor childrenSqlDescriptor = descriptors.get(s);
                        TopiaEntitySqlTable childrenTable = childrenSqlDescriptor.getTable();
                        List newSelectors = new LinkedList<>();
                        int index = 0;
                        for (TopiaEntitySqlSelector selector : childrenTable.getSelectors()) {
                            TopiaEntitySqlSelector parentSelector = selectors.get(index++);
                            TopiaEntitySqlSelector newSelector = TopiaEntitySqlSelector.builder()
                                    .setFromClause(selector.getFromClause())
                                    .setWhereClauseAlias("") // no where clause (will be computed by delete plan builder)
                                    .addJoinClause(selector.getJoinClauses().substring(0, selector.getJoinClauses().length() - parentSelector.getJoinClauses().length()))
                                    .setReverseSelector(selector.isReverseSelector())
                                    .build();
                            newSelectors.add(newSelector);
                        }
                        TopiaEntitySqlTable newChildrenTable = childrenTable.changeSelectors(newSelectors);
                        extraTableToDelete.put(entityName, newChildrenTable);
                    }
                }
            }
        }
    }

    protected TopiaEntitySqlCopyPlan createCopyPlan(TopiaMetadataEntity entity) {
        String type = entity.getFullyQualifiedName();
        List replicationOrder = replicationOrderByType.get(type);
        TopiaEntitySqlDescriptors replicationOrderDescriptors = getDescriptors(replicationOrder);
        TopiaEntitySqlCopyPlanBuilder builder = new TopiaEntitySqlCopyPlanBuilderForEntryPoint(replicationOrderDescriptors);
        TopiaEntitySqlCopyPlan plan = builder.build();
        generateReferentialShell(plan);
        return plan;
    }

    protected TopiaEntitySqlReplicatePlan createReplicatePlan(TopiaMetadataEntity entity, TopiaEntitySqlDescriptor descriptor) {
        String type = entity.getFullyQualifiedName();
        List replicationOrder = replicationOrderByType.get(type);
        TopiaEntitySqlDescriptors replicationOrderDescriptors = getDescriptors(replicationOrder);
        TopiaEntitySqlReplicatePlanBuilder builder = new TopiaEntitySqlReplicatePlanBuilder(metadataModel, standaloneLiterals, fqnToLiteral, replicationOrderDescriptors, descriptor, mandatoryReverseCompositions);
        return builder.build();
    }

    protected TopiaEntitySqlCopyPlan createStandaloneCopyPlan() {
        TopiaEntitySqlDescriptors replicationOrderDescriptors = getDescriptors(replicationOrderWithStandalone);
        TopiaEntitySqlCopyPlanBuilder builder = new TopiaEntitySqlCopyPlanBuilderStandalone(replicationOrderDescriptors);
        return builder.build();
    }

    public TopiaEntitySqlDescriptors getDescriptors(Collection fqnCollection) {
        List builder = new LinkedList<>();
        for (String fqn : Objects.requireNonNull(fqnCollection)) {
            builder.add(descriptors.get(fqn));
        }
        return new TopiaEntitySqlDescriptors(Collections.unmodifiableList(builder));
    }

    public TopiaEntitySqlTable generateTable(TopiaMetadataEntity entity, Collection paths) {
        List selectors = new LinkedList<>();
        if (paths == null || paths.isEmpty()) {
            selectors.add(generateSimplePathSelector(entity));
        } else {
            for (TopiaMetadataEntityPath path : paths) {
                TopiaEntitySqlSelector selector = generatePathSelector(path);
                selectors.add(selector);
            }
        }
        this.selectors.putAll(entity, selectors);
        return createTopiaEntitySqlTable(entity, selectors);
    }

    public List generateAssociations(TopiaMetadataEntity entity, boolean noPath) {
        List result = new LinkedList<>();
        Set associations = metadataModel.getAssociations(entity);
        for (TopiaMetadataAssociation association : associations) {
            result.add(createTopiaEntitySqlAssociationTable(association, generateAssociationSelector(entity, association, noPath)));
        }
        return result;
    }

    public List generateReverseCompositions(TopiaMetadataEntity entity, boolean noPath) {
        String type = entity.getFullyQualifiedName();
        List mandatoryReverseCompositions = this.mandatoryReverseCompositions.get(type);

        List result = new LinkedList<>();
        if (!mandatoryReverseCompositions.isEmpty()) {
            for (TopiaMetadataComposition association : mandatoryReverseCompositions) {
                List selector = generateReverseCompositionSelector(entity, association, noPath);
                result.add(createTopiaEntitySqlReverseCompositionTable(association, selector));
            }
        }
        return result.isEmpty() ? null : result;
    }

    public List generateReverseAssociations(TopiaMetadataEntity entity, boolean noPath) {
        String type = entity.getFullyQualifiedName();
        List reverseAssociations = this.reverseAssociations.get(type);
        List reverseCompositions = this.reverseCompositions.get(type);

        List result = new LinkedList<>();
        if (!reverseAssociations.isEmpty()) {
            for (TopiaMetadataReverseAssociation association : reverseAssociations) {
                List selector = generateReverseAssociationSelector(entity, association, noPath);
                result.add(createTopiaEntitySqlReverseAssociationTable(association, selector));
            }
        }
        if (!reverseCompositions.isEmpty()) {
            for (TopiaMetadataComposition association : reverseCompositions) {
                List selector = generateReverseAssociationSelector(entity, association, noPath);
                result.add(createTopiaEntitySqlReverseAssociationTable(association, selector));
            }
        }
        return result.isEmpty() ? null : result;
    }

    public List generateSimpleAssociations(TopiaMetadataEntity entity, boolean noPath) {
        List result = new LinkedList<>();
        Set associations = metadataModel.getSimpleAssociations(entity);
        for (TopiaMetadataSimpleAssociation association : associations) {
            result.add(createTopiaEntitySqlSimpleAssociationTable(entity, association.getTableName(), association.getPropertyName(), association.getTargetPropertyName(), generateSimpleAssociationSelector(entity, association, noPath)));
        }
        return result;
    }

    public List generateReverseCompositionSelector(TopiaMetadataEntity entity, TopiaMetadataComposition association, boolean noPath) {
        List result = new LinkedList<>();
        List selectors = this.selectors.get(entity);
        TopiaEntitySqlSelector prefix = generateReverseCompositionSelector(association);
        if (noPath) {
            result.add(prefix);
        } else {
            for (TopiaEntitySqlSelector selector : selectors) {
                result.add(generateReverseCompositionSelector(association, prefix, selector));
            }
        }
        return result;
    }

    public List generateAssociationSelector(TopiaMetadataEntity entity, TopiaMetadataAssociation association, boolean noPath) {
        List result = new LinkedList<>();
        List selectors = this.selectors.get(entity);
        TopiaEntitySqlSelector prefix = generateAssociationSelector(association);
        if (noPath) {
            result.add(prefix);
        } else {
            for (TopiaEntitySqlSelector selector : selectors) {
                result.add(generateAssociationSelector(association, prefix, selector));
            }
        }
        return result;
    }

    public List generateReverseAssociationSelector(TopiaMetadataEntity entity, TopiaMetadataReverseAssociation association, boolean noPath) {
        List result = new LinkedList<>();
        List selectors = this.selectors.get(entity);
        TopiaEntitySqlSelector prefix = generateReverseAssociationSelector(association);
        if (noPath) {
            result.add(prefix);
        } else {
            for (TopiaEntitySqlSelector selector : selectors) {
                result.add(generateReverseAssociationSelector(association, prefix, selector));
            }
        }
        return result;
    }

    public List generateReverseAssociationSelector(TopiaMetadataEntity entity, TopiaMetadataComposition association, boolean noPath) {
        List result = new LinkedList<>();
        List selectors = this.selectors.get(entity);
        TopiaEntitySqlSelector prefix = generateReverseAssociationSelector(association);
        if (noPath) {
            result.add(prefix);
        } else {
            for (TopiaEntitySqlSelector selector : selectors) {
                result.add(generateReverseAssociationSelector(association, prefix, selector));
            }
        }
        return result;
    }

    public List generateSimpleAssociationSelector(TopiaMetadataEntity entity, TopiaMetadataSimpleAssociation association, boolean noPath) {
        List result = new LinkedList<>();
        List selectors = this.selectors.get(entity);
        TopiaEntitySqlSelector prefix = generateSimpleAssociationSelector(association);
        if (noPath) {
            result.add(prefix);
        } else {
            for (TopiaEntitySqlSelector selector : selectors) {
                result.add(generateSimpleAssociationSelector(association, prefix, selector));
            }
        }
        return result;
    }

    private TopiaEntitySqlSelector generatePathSelector(TopiaMetadataEntityPath path) {
        // take links in reverse order
        List links = path.getReverseLinks();
        TopiaMetadataLink previousLink = null;
        Iterator iterator = links.iterator();
        int index = 0;
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        if (path.getLastLink() instanceof TopiaMetadataReverseAssociation) {
            builder.reverseSelector();
        }
        while (iterator.hasNext()) {
            TopiaMetadataLink link = iterator.next();
            boolean addJoin = true;
            if (previousLink == null) {
                // first link
                builder.setFromClause(String.format(SQL_FROM, link.getTarget().getDbSchemaName(), link.getTarget().getDbTableName()));
                if (!iterator.hasNext()) {
                    addJoin = false;
                }
            } else {
                if (link instanceof TopiaMetadataReverseAssociation && !(previousLink instanceof TopiaMetadataReverseAssociation)) {
                    // this join has been avoid (join directly on both inner links)
                    addJoin = false;
                    // we are also on a reverse selector
                    builder.reverseSelector();
                } else if (link instanceof TopiaMetadataAssociation && !iterator.hasNext()) {
                    // this last join has no need to be
                    addJoin = false;
                }
            }
            if (addJoin) {
                String tableOwnerJoinColumn;
                String tableTargetJoinColumn = TopiaEntity.PROPERTY_TOPIA_ID;
                TopiaMetadataEntity tableOwner;
                TopiaMetadataEntity tableTarget;
                if (link instanceof TopiaMetadataReverseAssociation) {
                    tableOwner = link.getTarget();
                    tableTarget = link.getOwner();
                    tableOwnerJoinColumn = TopiaEntity.PROPERTY_TOPIA_ID;
                    tableTargetJoinColumn = link.getTarget().getDbTableName();
                } else {
                    tableOwner = link.getTarget();
                    tableTarget = link.getOwner();
                    tableOwnerJoinColumn = link.getOwner().getDbTableName();

                    if (iterator.hasNext()) {
                        TopiaMetadataLink nextLink = links.get(index + 1);
                        if (nextLink instanceof TopiaMetadataReverseAssociation) {
                            // can join with next target
                            tableTarget = nextLink.getOwner();
                            tableOwner = link.getTarget();
                            tableTargetJoinColumn = nextLink.getOwner().getDbColumnName(nextLink.getTargetPropertyName());
                        }
                    }
                }
                tableOwnerJoinColumn = tableOwner.getDbColumnName(tableOwnerJoinColumn);
                builder.addJoinClause(String.format(SQL_INNER_JOIN,
                                                    tableTarget.getDbSchemaName(),
                                                    tableTarget.getDbTableName(),
                                                    tableTargetJoinColumn,
                                                    tableOwner.getDbTableName(),
                                                    tableOwnerJoinColumn));
            }
            previousLink = link;
            index++;
        }
        TopiaMetadataLink lastLink = Objects.requireNonNull(previousLink);
        TopiaMetadataEntity lastLinkOwner = lastLink.getOwner();
        String equalsTable = lastLinkOwner.getDbTableName();
        String equalsColumn = TopiaEntity.PROPERTY_TOPIA_ID;
        if (!(lastLink instanceof TopiaMetadataReverseAssociation)) {
            TopiaMetadataEntity lastLinkTarget = lastLink.getTarget();
            equalsTable = lastLinkTarget.getDbTableName();
            equalsColumn = lastLinkTarget.getDbColumnName(lastLinkOwner.getDbTableName());
        }
        builder.setWhereClauseAlias(String.format(SQL_WHERE_CLAUSE_ALIAS, equalsTable, equalsColumn));
        return builder.build();
    }

    private TopiaEntitySqlSelector generateSimplePathSelector(TopiaMetadataEntity entity) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        builder.setFromClause(String.format(SQL_FROM, entity.getDbSchemaName(), entity.getDbTableName()));
        builder.setWhereClauseAlias(String.format(SQL_WHERE_CLAUSE_ALIAS, entity.getDbTableName(), TopiaEntity.PROPERTY_TOPIA_ID));
        return builder.build();
    }

    private TopiaEntitySqlSelector generateAssociationSelector(TopiaMetadataAssociation association) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = owner.getDbColumnName(association.getSourceDbName());
        builder.setFromClause(String.format(SQL_FROM, owner.getDbSchemaName(), association.getTableName()));
        builder.setWhereClauseAlias(String.format(SQL_WHERE_CLAUSE_ALIAS, association.getTableName(), columnName));
        return builder.build();
    }

    private TopiaEntitySqlSelector generateAssociationSelector(TopiaMetadataAssociation association, TopiaEntitySqlSelector prefix, TopiaEntitySqlSelector selector) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        builder.setFromClause(prefix.getFromClause());
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = owner.getDbColumnName(association.getSourceDbName());
        builder.addJoinClause(String.format(SQL_INNER_JOIN,
                                            owner.getDbSchemaName(),
                                            owner.getDbTableName(),
                                            TopiaEntity.PROPERTY_TOPIA_ID,
                                            association.getTableName(),
                                            columnName));
        builder.addJoinClause(selector.getJoinClauses());
        builder.setWhereClauseAlias(selector.getWhereClauseAlias());
        return builder.build();
    }

    private TopiaEntitySqlSelector generateReverseCompositionSelector(TopiaMetadataComposition association) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = association.getTargetDbName();
        builder.setFromClause(String.format(SQL_FROM, owner.getDbSchemaName(), owner.getDbTableName()));
        builder.setWhereClauseAlias(String.format(SQL_WHERE_CLAUSE_ALIAS, association.getTarget().getDbTableName(), columnName));
        return builder.build();
    }

    private TopiaEntitySqlSelector generateReverseCompositionSelector(TopiaMetadataComposition association, TopiaEntitySqlSelector prefix, TopiaEntitySqlSelector selector) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        builder.setFromClause(prefix.getFromClause());
        TopiaMetadataEntity owner = association.getTarget();
        String columnName = association.getTargetDbName();
        builder.addJoinClause(String.format(SQL_INNER_JOIN,
                                            owner.getDbSchemaName(),
                                            owner.getDbTableName(),
                                            TopiaEntity.PROPERTY_TOPIA_ID,
                                            association.getOwner().getDbTableName(),
                                            columnName));
        builder.addJoinClause(selector.getJoinClauses());
        builder.setWhereClauseAlias(selector.getWhereClauseAlias());
        return builder.build();
    }

    private TopiaEntitySqlSelector generateReverseAssociationSelector(TopiaMetadataReverseAssociation association) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = association.getTargetDbName();
        builder.setFromClause(String.format(SQL_FROM, owner.getDbSchemaName(), owner.getDbTableName()));
        builder.setWhereClauseAlias(String.format(SQL_WHERE_CLAUSE_ALIAS, owner.getDbTableName(), columnName));
        return builder.build();
    }

    private TopiaEntitySqlSelector generateReverseAssociationSelector(TopiaMetadataReverseAssociation association, TopiaEntitySqlSelector prefix, TopiaEntitySqlSelector selector) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        builder.setFromClause(prefix.getFromClause());
        TopiaMetadataEntity owner = association.getTarget();
        String columnName = association.getTargetDbName();
        builder.addJoinClause(String.format(SQL_INNER_JOIN,
                                            owner.getDbSchemaName(),
                                            owner.getDbTableName(),
                                            TopiaEntity.PROPERTY_TOPIA_ID,
                                            association.getOwner().getDbTableName(),
                                            columnName));
        builder.addJoinClause(selector.getJoinClauses());
        builder.setWhereClauseAlias(selector.getWhereClauseAlias());
        return builder.build();
    }

    private TopiaEntitySqlSelector generateReverseAssociationSelector(TopiaMetadataComposition association) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = association.getTargetDbName();
        builder.setFromClause(String.format(SQL_FROM, owner.getDbSchemaName(), owner.getDbTableName()));
        builder.setWhereClauseAlias(String.format(SQL_WHERE_CLAUSE_ALIAS, owner.getDbTableName(), columnName));
        return builder.build();
    }

    private TopiaEntitySqlSelector generateReverseAssociationSelector(TopiaMetadataComposition association, TopiaEntitySqlSelector prefix, TopiaEntitySqlSelector selector) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        builder.setFromClause(prefix.getFromClause());
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = association.getTargetDbName();
        builder.addJoinClause(String.format(SQL_INNER_JOIN,
                                            owner.getDbSchemaName(),
                                            owner.getDbTableName(),
                                            columnName,
                                            association.getTarget().getDbTableName(),
                                            TopiaEntity.PROPERTY_TOPIA_ID));
        builder.addJoinClause(selector.getJoinClauses());
        builder.setWhereClauseAlias(selector.getWhereClauseAlias());
        return builder.build();
    }

    private TopiaEntitySqlSelector generateSimpleAssociationSelector(TopiaMetadataSimpleAssociation association) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = association.getSourceDbName();
        builder.setFromClause(String.format(SQL_FROM, owner.getDbSchemaName(), association.getTableName()));
        builder.setWhereClauseAlias(String.format(SQL_WHERE_CLAUSE_ALIAS, association.getTableName(), columnName));
        return builder.build();
    }

    private TopiaEntitySqlSelector generateSimpleAssociationSelector(TopiaMetadataSimpleAssociation association, TopiaEntitySqlSelector prefix, TopiaEntitySqlSelector selector) {
        TopiaEntitySqlSelector.Builder builder = TopiaEntitySqlSelector.builder();
        builder.setFromClause(prefix.getFromClause());
        TopiaMetadataEntity owner = association.getOwner();
        String columnName = owner.getDbColumnName(association.getSourceDbName());
        builder.addJoinClause(String.format(SQL_INNER_JOIN,
                                            owner.getDbSchemaName(),
                                            owner.getDbTableName(),
                                            TopiaEntity.PROPERTY_TOPIA_ID,
                                            association.getTableName(),
                                            columnName));
        builder.addJoinClause(selector.getJoinClauses());
        builder.setWhereClauseAlias(selector.getWhereClauseAlias());
        return builder.build();
    }

    protected void generateUsageAndBlob(ObjectModelClass input) {

        Map result = new LinkedHashMap<>();
//        result.put("entityType", input.getQualifiedName());
        String inputQualifiedName = input.getQualifiedName();
        usages.put(inputQualifiedName, result);
        List reverseComposition = new LinkedList<>();
        List reverseMandatoryComposition = new LinkedList<>();
        List reverseManyToManyAssociation = new LinkedList<>();
        List reverseOneToManyAssociation = new LinkedList<>();
        List reverseOneToOneComposition = new LinkedList<>();
        List reverseAssociation = new LinkedList<>();

        String literalName = getTemplateHelper().getEntityEnumLiteralName(input);
        TopiaMetadataEntity metadataEntity = metadataModel.getEntity(literalName);
        if (metadataEntity.withBlob()) {
            Set blobProperties = metadataEntity.getBlobProperties();
            blobs.put(inputQualifiedName, new TopiaEntitySqlBlob(metadataEntity.getDbSchemaName(), metadataEntity.getDbTableName(), blobProperties.stream().map(metadataEntity::getDbColumnName).collect(Collectors.toList())));
        }
        Set reverseCompositions = metadataModel.getReverseCompositions(metadataEntity);
        if (reverseCompositions != null && reverseCompositions.size() > 0) {
            log.info(String.format("Add reverse compositions usage for: %s - %d", input.getName(), reverseCompositions.size()));
            TopiaTemplateHelper templateHelper = getTemplateHelper();
            for (TopiaMetadataComposition link : reverseCompositions) {
                ObjectModelClass targetObjectModelClass = metadataToEntityMapping.get(link.getOwner());
                String targetPropertyName = link.getTargetPropertyName();
                ObjectModelAttribute targetAttribute = targetObjectModelClass.getAttribute(targetPropertyName);
                boolean attributeNotNull = templateHelper.isAttributeNotNull(targetAttribute);
                String targetClass = targetObjectModelClass.getQualifiedName();
                if (attributeNotNull) {
                    reverseMandatoryComposition.add(targetClass + "~" + targetPropertyName);
                } else {
                    reverseComposition.add(targetClass + "~" + targetPropertyName);
                }
            }
        }
        Collection reverseAssociations = reverseAssociations2.get(metadataEntity);
        if (reverseAssociations.size() > 0) {
            log.info(String.format("Add reverse associations usage for: %s - %d", input.getName(), reverseAssociations.size()));
            for (TopiaMetadataReverseAssociation link : reverseAssociations) {
                String targetClass = metadataToEntityMapping.get(link.getOwner()).getQualifiedName();
                String targetPropertyName = link.getTargetPropertyName();
                reverseAssociation.add(targetClass + "~" + targetPropertyName);
            }
        }
        Set reverseManyToManyAssociations = metadataModel.getReverseManyToManyAssociations(metadataEntity);
        if (reverseManyToManyAssociations != null && reverseManyToManyAssociations.size() > 0) {
            log.info(String.format("Add reverse many to many associations usage for: %s - %d", input.getName(), reverseManyToManyAssociations.size()));
            for (TopiaMetadataAssociation link : reverseManyToManyAssociations) {
                String targetClass = metadataToEntityMapping.get(link.getOwner()).getQualifiedName();
                String targetPropertyName = link.getTargetPropertyName();
                reverseManyToManyAssociation.add(targetClass + "~" + targetPropertyName);
            }
        }
        Set reverseOneToManyAssociations = metadataModel.getReverseOneToManyAssociations(metadataEntity);
        if (reverseOneToManyAssociations != null && reverseOneToManyAssociations.size() > 0) {
            log.info(String.format("Add reverse one to many associations usage for: %s - %d", input.getName(), reverseOneToManyAssociations.size()));
            for (TopiaMetadataAssociation link : reverseOneToManyAssociations) {
                String targetClass = metadataToEntityMapping.get(link.getOwner()).getQualifiedName();
                String targetPropertyName = link.getTargetPropertyName();
                reverseOneToManyAssociation.add(targetClass + "~" + targetPropertyName);
            }
        }

        Set reverseOneToOneCompositions = metadataModel.getReverseOneToOneAssociations(metadataEntity);
        if (reverseOneToOneCompositions != null && reverseOneToOneCompositions.size() > 0) {
            log.info(String.format("Add reverse one to one compositions usage for: %s - %d", input.getName(), reverseOneToOneCompositions.size()));
            for (TopiaMetadataOneToOneComposition link : reverseOneToOneCompositions) {
                String targetClass = metadataToEntityMapping.get(link.getOwner()).getQualifiedName();
                String targetPropertyName = link.getTargetPropertyName();
                reverseOneToOneComposition.add(targetClass + "~" + targetPropertyName);
            }
        }
        if (!reverseComposition.isEmpty()) {
            result.put("reverseCompositions", reverseComposition);
        }
        if (!reverseMandatoryComposition.isEmpty()) {
            result.put("reverseMandatoryCompositions", reverseMandatoryComposition);
        }
        if (!reverseManyToManyAssociation.isEmpty()) {
            result.put("reverseManyToManyAssociations", reverseManyToManyAssociation);
        }
        if (!reverseOneToManyAssociation.isEmpty()) {
            result.put("reverseOneToManyAssociations", reverseOneToManyAssociation);
        }
        if (!reverseOneToOneComposition.isEmpty()) {
            result.put("reverseOneToOneCompositions", reverseOneToOneComposition);
        }
        if (!reverseAssociation.isEmpty()) {
            result.put("reverseAssociations", reverseAssociation);
        }
    }

    protected void generateReferentialShell(TopiaEntitySqlCopyPlan plan) {
        for (TopiaEntitySqlCopyPlanTask task : plan) {
            Map> columns = generateReferentialShell(task);
            if (!columns.isEmpty()) {
                Map extra = columns.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> String.join(",", e.getValue())));
                task.setExtra(Map.of(TopiaEntitySqlModelResource.REFERENTIAL_SHELL, extra));
            }
        }
    }

    protected Map> generateReferentialShell(TopiaEntitySqlCopyPlanTask task) {
        Map> result = new TreeMap<>();
        String tableName = task.getSchemaAndTableName();
        String literal = gavToLiteral.get(tableName);
        int i = literal.indexOf("::");
        if (i == -1) {
            // simple table
            TopiaMetadataEntity currentType = metadataModel.getEntity(literal);
            Map properties = currentType.getProperties();
            Map manyToOneAssociations = currentType.getManyToOneAssociations();
            Map manyToManyAssociations = currentType.getManyToManyAssociations();
            for (String columnName : task.getColumnNames()) {
                if (properties.containsKey(columnName)) {
                    continue;
                }
                if (manyToOneAssociations.containsKey(columnName)) {
                    addReferentialColum(currentType, result, manyToOneAssociations, columnName);
                } else if (manyToManyAssociations.containsKey(columnName)) {
                    addReferentialColum(currentType, result, manyToManyAssociations, columnName);
                }
            }
        } else {
            // association table
            String target = literal.substring(i + 2);
            literal = literal.substring(0, i);
            TopiaMetadataEntity currentType = metadataModel.getEntity(literal);
            Map dbManyToManyAssociationsTableName = currentType.getDbManyToManyAssociationsTableName();
            String type = null;
            for (Map.Entry entry : dbManyToManyAssociationsTableName.entrySet()) {
                if (entry.getValue().equals(target)) {
                    type = entry.getKey();
                    break;
                }
            }
            if (type != null) {
                Map manyToManyAssociations = currentType.getManyToManyAssociations();
                addReferentialColum(currentType, result, manyToManyAssociations, type);
            }
        }
        return result;
    }

    protected void addReferentialColum(TopiaMetadataEntity currentType, Map> result, Map manyToManyAssociations, String columnName) {
        String targetLiteral = manyToManyAssociations.get(columnName);
        TopiaMetadataEntity targetEntity = metadataModel.getEntity(targetLiteral);
        String targetFullyQualifiedName = targetEntity.getFullyQualifiedName();
        if (targetFullyQualifiedName.contains(".referential.")) {
            columnName = currentType.getDbColumnName(columnName);
            result.computeIfAbsent(targetFullyQualifiedName, e -> new TreeSet<>()).add(columnName);
        }
    }

    enum Step {
        model,
        copyPlan,
        replicatePlan,
        deletePlan,
        usage,
        blob,
        differentialMetaModel;
    }

    private TopiaEntitySqlTable createTopiaEntitySqlTable(TopiaMetadataEntity metadataEntity, List selectors) {
        return new TopiaEntitySqlTable(metadataEntity.getFullyQualifiedName(),
                                       metadataEntity.getDbSchemaName(),
                                       metadataEntity.getDbTableName(),
                                       metadataEntity.getAllDbColumnNames(),
                                       selectors,
                                       metadataEntity.getBlobProperties(),
                                       metadataEntity.getDbSimplePropertiesTypes(),
                                       metadataEntity.getDbDecimalPrecisions(),
                                       metadataEntity.getDbEnumerationPropertiesUsingName(),
                                       metadataEntity.getOptionalRecursiveProperty().orElse(null));
    }


    private TopiaEntitySqlAssociationTable createTopiaEntitySqlAssociationTable(TopiaMetadataAssociation metadataAssociation, List selectors) {
        return new TopiaEntitySqlAssociationTable(metadataAssociation.getOwner().getFullyQualifiedName(),
                                                  metadataAssociation.getOwner().getDbSchemaName(),
                                                  metadataAssociation.getTableName(),
                                                  new LinkedHashSet<>(List.of(metadataAssociation.getSourceDbName(), metadataAssociation.getTargetDbName())),
                                                  selectors,
                                                  metadataAssociation.getSourceDbName());
    }

    private TopiaEntitySqlReverseCompositionTable createTopiaEntitySqlReverseCompositionTable(TopiaMetadataComposition link, List selectors) {
        return new TopiaEntitySqlReverseCompositionTable(link.getOwner().getFullyQualifiedName(),
                                                         link.getOwner().getDbSchemaName(),
                                                         link.getOwner().getDbTableName(),
                                                         new LinkedHashSet<>(List.of(TopiaEntity.PROPERTY_TOPIA_ID, link.getTargetDbName())),
                                                         selectors,
                                                         link.getTargetDbName());
    }


    private TopiaEntitySqlReverseAssociationTable createTopiaEntitySqlReverseAssociationTable(TopiaMetadataComposition link, List selectors) {
        return new TopiaEntitySqlReverseAssociationTable(link.getOwner().getFullyQualifiedName(),
                                                         link.getOwner().getDbSchemaName(),
                                                         link.getOwner().getDbTableName(),
                                                         new LinkedHashSet<>(List.of(TopiaEntity.PROPERTY_TOPIA_ID, link.getTargetDbName())),
                                                         selectors,
                                                         link.getTargetDbName());
    }

    private TopiaEntitySqlReverseAssociationTable createTopiaEntitySqlReverseAssociationTable(TopiaMetadataReverseAssociation link, List selectors) {
        return new TopiaEntitySqlReverseAssociationTable(link.getOwner().getFullyQualifiedName(),
                                                         link.getOwner().getDbSchemaName(),
                                                         link.getOwner().getDbTableName(),
                                                         new LinkedHashSet<>(List.of(TopiaEntity.PROPERTY_TOPIA_ID, link.getTargetDbName())),
                                                         selectors,
                                                         link.getTargetDbName());
    }


    private TopiaEntitySqlSimpleAssociationTable createTopiaEntitySqlSimpleAssociationTable(TopiaMetadataEntity metadataEntity,
                                                                                            String tableName,
                                                                                            String propertyName,
                                                                                            String joinColumnName,
                                                                                            List selectors) {
        return new TopiaEntitySqlSimpleAssociationTable(tableName,
                                                        metadataEntity.getDbSchemaName(),
                                                        tableName,
                                                        new LinkedHashSet<>(List.of(joinColumnName, metadataEntity.getDbColumnName(propertyName))),
                                                        selectors, joinColumnName);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy