This is an automated email from the git hooks/post-receive script. New commit to branch feature/3860_introduce_topiasqlbatchsupport in repository topia. See http://git.nuiton.org/topia.git commit 4b53efcd0f207d5e27cd66392eaf3f1fa0106c74 Author: Tony CHEMIT <chemit@codelutin.com> Date: Tue Jan 5 08:25:22 2016 +0100 Add TopiaSqlTable API --- .../service/sql/batch/tables/TopiaSqlTable.java | 138 +++++++ .../service/sql/batch/tables/TopiaSqlTables.java | 406 +++++++++++++++++++++ .../sql/batch/tables/TopiaSqlTablesFactory.java | 193 ++++++++++ 3 files changed, 737 insertions(+) diff --git a/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTable.java b/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTable.java new file mode 100644 index 0000000..fd04e9e --- /dev/null +++ b/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTable.java @@ -0,0 +1,138 @@ +package org.nuiton.topia.service.sql.batch.tables; + +/* + * #%L + * ToPIA :: Service Replication + * %% + * Copyright (C) 2004 - 2015 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSet; + +import java.util.Objects; + +/** + * Created on 30/12/15. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 3.0.1 + */ +public class TopiaSqlTable { + + /** + * Table schema name. + */ + protected final String schemaName; + + /** + * Table name. + */ + protected final String tableName; + + /** + * Fully table name (including the schema name). + */ + protected final String fullyTableName; + + /** + * From clause. + */ + protected final String fromClause; + + /** + * Where clause alias. + */ + protected final String whereClauseAlias; + + /** + * Join clauses. + */ + protected final ImmutableSet<String> joinClauses; + + public TopiaSqlTable(String schemaName, + String tableName, + String fromClause, + String whereClauseAlias, + ImmutableSet<String> joinClauses) { + this.schemaName = schemaName.toLowerCase(); + this.tableName = tableName.toLowerCase(); + this.fullyTableName = this.schemaName + "." + this.tableName; + this.fromClause = fromClause; + this.whereClauseAlias = whereClauseAlias; + this.joinClauses = joinClauses; + } + + public String getSchemaName() { + return schemaName; + } + + public String getTableName() { + return tableName; + } + + public String getFullyTableName() { + return fullyTableName; + } + + public String getFromClause() { + return fromClause; + } + + public String getWhereClauseAlias() { + return whereClauseAlias; + } + + public String getWhereClause(ImmutableSet<String> ids) { + String result = whereClauseAlias; + if (ids.size() == 1) { + result += " = ?"; + } else { + String in = ""; + for (String ignored : ids) { + in += ", ?"; + } + result += " IN (" + in.substring(2) + ")"; + } + return result; + } + + public ImmutableSet<String> getJoinClauses() { + return joinClauses; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TopiaSqlTable that = (TopiaSqlTable) o; + return Objects.equals(fullyTableName, that.fullyTableName); + } + + @Override + public int hashCode() { + return Objects.hash(fullyTableName); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("fullyTableName", fullyTableName) + .toString(); + } +} diff --git a/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTables.java b/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTables.java new file mode 100644 index 0000000..5498284 --- /dev/null +++ b/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTables.java @@ -0,0 +1,406 @@ +package org.nuiton.topia.service.sql.batch.tables; + +/* + * #%L + * ToPIA :: Service Sql batch + * %% + * Copyright (C) 2004 - 2016 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +/** + * Created on 01/01/16. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 3.0.1 + */ +public class TopiaSqlTables implements Iterable<TopiaSqlTable> { + + protected final ImmutableMap<String, TopiaSqlTable> tablesByFullyTableName; + protected final ImmutableSet<TopiaSqlTable> orderedTables; + + public TopiaSqlTables(ImmutableMap<String, TopiaSqlTable> tablesByFullyTableName, + ImmutableSet<TopiaSqlTable> orderedTables) { + this.tablesByFullyTableName = tablesByFullyTableName; + this.orderedTables = orderedTables; + } + + public static Builder builder() { + return new BuilderImpl(); + } + + public static BuilderStepOnTable builder(TopiaEntityEnum mainEntityEnum) { + return builder().addMainTable(mainEntityEnum); + } + + public TopiaSqlTable getTable(String key) { + return tablesByFullyTableName.get(key); + } + + @Override + public Iterator<TopiaSqlTable> iterator() { + return orderedTables.iterator(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("orderedTables", orderedTables) + .toString(); + } + + /** + * Created on 02/01/16. + * + * @author Tony Chemit - chemit@codelutin.com + */ + public interface Builder { + + BuilderStepOnTable addMainTable(TopiaEntityEnum entityEnum); + + TopiaSqlTables build(); + + } + + public interface BuilderStepOnTable extends Builder { + + BuilderStepOnTable addJoinTable(TopiaEntityEnum entityEnum); + + BuilderStepOnTable addAndEnterJoinTable(TopiaEntityEnum entityEnum); + + BuilderStepOnTable addReverseJoinTable(TopiaEntityEnum entityEnum); + + BuilderStepOnTable addAndEnterReverseJoinTable(TopiaEntityEnum entityEnum); + + BuilderStepOnTable addAssociationTable(String associationName); + + BuilderStepOnTable backToParent(); + + BuilderStepOnTable backToTable(TopiaEntityEnum entityEnum); + + BuilderStepOnTable checkCurrentTable(TopiaEntityEnum entityEnum); + } + + + /** + * Created on 01/01/16. + * + * @author Tony Chemit - chemit@codelutin.com + */ + protected static class BuilderImpl implements Builder { + + protected final TreeMap<String, TopiaSqlTable> tablesByFullyTableName; + + protected final TreeMap<Integer, TopiaSqlTable> tablesByOrder; + + protected int internalOrder; + + public BuilderImpl() { + this.tablesByFullyTableName = new TreeMap<>(); + this.tablesByOrder = new TreeMap<>(); + } + + @Override + public BuilderStepOnTable addMainTable(TopiaEntityEnum entityEnum) { + + String schemaName = entityEnum.dbSchemaName().toLowerCase(); + String tableName = entityEnum.dbTableName().toLowerCase(); + + //TODO check that this table is not already registred + String whereClauseAlias = tableName + ".topiaid"; + String fromClause = schemaName + "." + tableName + " " + tableName; + + registerTable(schemaName, + tableName, + whereClauseAlias, + fromClause, + ImmutableSet.<String>of()); + + return new BuilderStepOnTableImpl(null, entityEnum); + } + + @Override + public TopiaSqlTables build() { + + List<Integer> orders = Lists.newArrayList(tablesByOrder.keySet()); + Collections.sort(orders); + ImmutableSet.Builder<TopiaSqlTable> orderedTablesBuilder = ImmutableSet.builder(); + for (Integer order : orders) { + orderedTablesBuilder.add(tablesByOrder.get(order)); + } + + return new TopiaSqlTables(ImmutableMap.copyOf(tablesByFullyTableName), + orderedTablesBuilder.build()); + } + + protected Builder registerTable(String schemaName, + String tableName, + String whereClauseAlias, + String fromClause, + ImmutableSet<String> joinClauses) { + + //TODO check that this table is not already registred + + TopiaSqlTable table = new TopiaSqlTable( + schemaName, + tableName, + fromClause, + whereClauseAlias, + joinClauses); + + tablesByFullyTableName.put(table.getFullyTableName(), table); + tablesByOrder.put(internalOrder++, table); + return this; + } + + protected TopiaSqlTable getTable(String key) { + return tablesByFullyTableName.get(key); + } + + protected TopiaSqlTable getTable(TopiaEntityEnum entityEnum) { + String key = getFullyTableName(entityEnum); + return tablesByFullyTableName.get(key); + } + + protected String getFullyTableName(TopiaEntityEnum entityEnum) { + return entityEnum.dbSchemaName().toLowerCase() + "." + entityEnum.dbTableName().toLowerCase(); + } + + protected String getAssociationTableName(String tableName, String parentTableName) { + String associationTableName; + if (tableName.compareTo(parentTableName) < 0) { + associationTableName = tableName + "_" + parentTableName; + } else { + associationTableName = parentTableName + "_" + tableName; + } + return associationTableName; + } + + protected class BuilderStepOnTableImpl implements Builder, BuilderStepOnTable { + + protected final BuilderStepOnTableImpl parent; + + protected final TopiaEntityEnum tableEntityEnum; + + protected BuilderStepOnTableImpl(BuilderStepOnTableImpl parent, TopiaEntityEnum tableEntityEnum) { + this.parent = parent; + this.tableEntityEnum = tableEntityEnum; + } + + @Override + public BuilderStepOnTable addMainTable(TopiaEntityEnum entityEnum) { + return BuilderImpl.this.addMainTable(entityEnum); + } + + @Override + public TopiaSqlTables build() { + return BuilderImpl.this.build(); + } + + @Override + public BuilderStepOnTable addJoinTable(TopiaEntityEnum entityEnum) { + + TopiaSqlTable parentTable = getTable(); + + String schemaName = entityEnum.dbSchemaName().toLowerCase(); + String tableName = entityEnum.dbTableName().toLowerCase(); + + String parentTableName = parentTable.getTableName(); + + String whereClauseAlias; + String fromClause; + ImmutableSet<String> joinClauses; + + if (parent == null) { + + // parent table is main (no join on it) + // we can directly use the target table to join + + whereClauseAlias = tableName + "." + parentTableName; + fromClause = schemaName + "." + tableName + " " + tableName; + joinClauses = ImmutableSet.of(); + + } else { + + // simple join table + + whereClauseAlias = parentTable.getWhereClauseAlias(); + fromClause = parentTable.getFromClause(); + String joinClause = " INNER JOIN " + schemaName + "." + tableName + " " + tableName + " ON " + tableName + "." + parentTableName + " = " + parentTableName + ".topiaId"; + + joinClauses = addJoinCause(parentTable.getJoinClauses(), joinClause); + + } + + registerTable(schemaName, + tableName, + whereClauseAlias, + fromClause, + joinClauses); + + return this; + } + + @Override + public BuilderStepOnTable addAndEnterJoinTable(TopiaEntityEnum entityEnum) { + addJoinTable(entityEnum); + return new BuilderStepOnTableImpl(this, entityEnum); + } + + @Override + public BuilderStepOnTable addReverseJoinTable(TopiaEntityEnum entityEnum) { + + TopiaSqlTable parentTable = getTable(); + + String schemaName = entityEnum.dbSchemaName().toLowerCase(); + String tableName = entityEnum.dbTableName().toLowerCase(); + String whereClauseAlias = parentTable.getWhereClauseAlias(); + String fromClause = parentTable.getFromClause(); + + String parentTableName = parentTable.getTableName(); + String joinClause = " INNER JOIN " + schemaName + "." + tableName + " " + tableName + " ON " + tableName + ".topiaId = " + parentTableName + "." + tableName; + + ImmutableSet<String> joinClauses = addJoinCause(parentTable.getJoinClauses(), joinClause); + + registerTable( + schemaName, + tableName, + whereClauseAlias, + fromClause, + joinClauses); + + invertOrderWithParent(parentTable, entityEnum); + return this; + + } + + @Override + public BuilderStepOnTable addAndEnterReverseJoinTable(TopiaEntityEnum entityEnum) { + addReverseJoinTable(entityEnum); + return new BuilderStepOnTableImpl(this, entityEnum); + } + + @Override + public BuilderStepOnTable addAssociationTable(String associationName) { + + TopiaSqlTable parentTable = getTable(); + + String schemaName = tableEntityEnum.dbSchemaName().toLowerCase(); + String tableName = getAssociationTableName(associationName.toLowerCase(), parentTable.getTableName()); + String whereClauseAlias = parentTable.getWhereClauseAlias(); + String fromClause = parentTable.getFromClause(); + if (parentTable.getJoinClauses().isEmpty()) { + fromClause = schemaName + "." + tableName; + } + ImmutableSet<String> joinClauses; + + boolean addInnerJoin = parent != null; + if (addInnerJoin) { + + String parentTableName = parentTable.getTableName(); + String joinClause = " INNER JOIN " + schemaName + "." + tableName + " " + tableName + " ON " + tableName + "." + parentTableName + " = " + parentTableName + ".topiaId"; + joinClauses = addJoinCause(parentTable.getJoinClauses(), joinClause); + + } else { + joinClauses = parentTable.getJoinClauses(); + } + + registerTable(schemaName, + tableName, + whereClauseAlias, + fromClause, + joinClauses); + return this; + } + + @Override + public BuilderStepOnTable backToParent() { + Preconditions.checkState(parent != null, "Could not find a parent table"); + return parent; + } + + @Override + public BuilderStepOnTable backToTable(TopiaEntityEnum entityEnum) { + + BuilderStepOnTable table; + if (Objects.equals(tableEntityEnum, entityEnum)) { + table = this; + } else { + Preconditions.checkState(parent != null, "Could not find a perent table of type: " + entityEnum); + table = parent.backToTable(entityEnum); + } + return table; + } + + @Override + public BuilderStepOnTable checkCurrentTable(TopiaEntityEnum entityEnum) { + Preconditions.checkState(tableEntityEnum.equals(entityEnum), "Current table should be " + entityEnum + ", but was " + tableEntityEnum); + return this; + } + + protected TopiaSqlTable getTable() { + return BuilderImpl.this.getTable(tableEntityEnum); + } + + protected int getTableOrder(TopiaSqlTable table) { + + for (Map.Entry<Integer, TopiaSqlTable> entry : tablesByOrder.entrySet()) { + + if (table.equals(entry.getValue())) { + return entry.getKey(); + } + } + + throw new IllegalStateException("Could not find table " + table.getFullyTableName()); + + } + + protected void invertOrderWithParent(TopiaSqlTable parentTable, TopiaEntityEnum entityEnum) { + + int parentTableOrder = getTableOrder(parentTable); + TopiaSqlTable table = BuilderImpl.this.getTable(entityEnum); + int tableOrder = getTableOrder(table); + tablesByOrder.put(parentTableOrder, table); + tablesByOrder.put(tableOrder, parentTable); + } + + protected ImmutableSet<String> addJoinCause(ImmutableSet<String> joinClauses, String joinClause) { + return ImmutableSet + .<String>builder() + .addAll(joinClauses) + .add(joinClause) + .build(); + } + + } + + } +} \ No newline at end of file diff --git a/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTablesFactory.java b/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTablesFactory.java new file mode 100644 index 0000000..9c4e489 --- /dev/null +++ b/topia-service-sql-batch/src/main/java/org/nuiton/topia/service/sql/batch/tables/TopiaSqlTablesFactory.java @@ -0,0 +1,193 @@ +package org.nuiton.topia.service.sql.batch.tables; + +/* + * #%L + * ToPIA :: Service Sql batch + * %% + * Copyright (C) 2004 - 2016 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.TopiaEntityEnumProvider; +import org.nuiton.topia.persistence.metadata.TopiaMetadataEntity; +import org.nuiton.topia.persistence.metadata.TopiaMetadataModel; +import org.nuiton.topia.persistence.metadata.TopiaMetadataModelVisitor; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Created on 04/01/16. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 3.0.1 + */ +public class TopiaSqlTablesFactory { + + /** + * Logger. + */ + private static final Log log = LogFactory.getLog(TopiaSqlTablesFactory.class); + + protected final TopiaMetadataModel model; + protected final TopiaEntityEnumProvider entityEnumProvider; + + public TopiaSqlTablesFactory(TopiaMetadataModel model, TopiaEntityEnumProvider entityEnumProvider) { + this.model = model; + this.entityEnumProvider = entityEnumProvider; + } + + public TopiaSqlTables newReplicateEntityTables(TopiaSqlTablesPredicate predicate, TopiaEntityEnum... entityEnums) { + + ReplicateTables tablesBuilder = new ReplicateTables(predicate); + return tablesBuilder.getTables(entityEnums); + + } + + public interface TopiaSqlTablesPredicate { + + boolean acceptEntity(TopiaMetadataEntity metadataEntity); + + boolean acceptAssociation(TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType); + + boolean acceptReversedAssociation(TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType); + + boolean acceptNmAssociation(TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType); + + } + + public class ReplicateTables implements TopiaMetadataModelVisitor { + + protected final TopiaSqlTablesPredicate predicate; + protected final Set<TopiaMetadataEntity> dones; + protected TopiaSqlTables.BuilderStepOnTable builder; + protected TopiaSqlTables tables; + + public ReplicateTables(TopiaSqlTablesPredicate predicate) { + this.predicate = predicate; + this.dones = new LinkedHashSet<>(); + } + + public TopiaSqlTables getTables(TopiaEntityEnum... entityEnums) { + visitModelStart(model); + for (TopiaEntityEnum entityEnum : entityEnums) { + TopiaMetadataEntity entity = model.getEntity(entityEnum.name()); + entity.accept(this, model); + } + visitModelEnd(model); + return tables; + } + + @Override + public void visitModelStart(TopiaMetadataModel metadataModel) { + } + + @Override + public void visitModelEnd(TopiaMetadataModel metadataModel) { + tables = builder.build(); + } + + @Override + public void visitEntiyStart(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity) { + + if (predicate.acceptEntity(metadataEntity)) { + + boolean added = dones.add(metadataEntity); + if (added) { + + log.info("E → " + metadataEntity.getType()); + TopiaEntityEnum entityEnum = entityEnumProvider.getEntityEnum(metadataEntity.getType()); + + builder = (builder == null ? TopiaSqlTables.builder() : builder).addMainTable(entityEnum); + } + } + + } + + @Override + public void visitEntiyEnd(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity) { + + if (dones.contains(metadataEntity)) { + log.info("E ← " + metadataEntity.getType()); + } + + } + + @Override + public void visitReversedAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) { + + if (predicate.acceptReversedAssociation(metadataEntity, propertyName, propertyType)) { + + TopiaEntityEnum entityEnum = entityEnumProvider.getEntityEnum(propertyType.getType()); + boolean withShell = propertyType.withShell(); + log.info(metadataEntity.getType() + "/" + propertyName + "→" + propertyType.getType() + " (withShell: " + withShell + ")"); + + if (withShell) { + builder = builder.addAndEnterReverseJoinTable(entityEnum); + visitChild(propertyType); + } else { + builder = builder.addReverseJoinTable(entityEnum); + } + } + } + + @Override + public void visitAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) { + + if (predicate.acceptAssociation(metadataEntity, propertyName, propertyType)) { + + TopiaEntityEnum entityEnum = entityEnumProvider.getEntityEnum(propertyType.getType()); + boolean withShell = propertyType.withShell(); + log.info(metadataEntity.getType() + "/" + propertyName + "→" + propertyType.getType() + " (withShell: " + withShell + ")"); + + if (withShell) { + builder = builder.addAndEnterJoinTable(entityEnum); + visitChild(propertyType); + } else { + builder = builder.addJoinTable(entityEnum); + } + } + + } + + @Override + public void visitNmAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) { + + if (predicate.acceptNmAssociation(metadataEntity, propertyName, propertyType)) { + + log.info(metadataEntity.getType() + "/" + propertyName + "→" + propertyType.getType()); + + builder = builder.addAssociationTable(propertyName); + } + + } + + @Override + public void visitRequired(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) { + } + + protected void visitChild(TopiaMetadataEntity propertyType) { + dones.add(propertyType); + propertyType.accept(this, model); + builder = builder.backToParent(); + } + + } +} -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.