org.nuiton.topia.templates.sql.EntitySqlDescriptorGenerator Maven / Gradle / Ivy
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