diff --git a/citydb-database-postgres/src/main/java/org/citydb/database/postgres/PostgresqlAdapter.java b/citydb-database-postgres/src/main/java/org/citydb/database/postgres/PostgresqlAdapter.java index 80fd3005..6abd0356 100644 --- a/citydb-database-postgres/src/main/java/org/citydb/database/postgres/PostgresqlAdapter.java +++ b/citydb-database-postgres/src/main/java/org/citydb/database/postgres/PostgresqlAdapter.java @@ -51,6 +51,6 @@ public int getDefaultPort() { @Override public String getConnectionString(String host, int port, String database) { return "jdbc:postgresql://" + host + ":" + port + "/" + database + - "?defaultRowFetchSize=10000&reWriteBatchedInserts=true"; + "?defaultRowFetchSize=1000&reWriteBatchedInserts=true"; } } diff --git a/citydb-database-postgres/src/main/java/org/citydb/database/postgres/SchemaAdapter.java b/citydb-database-postgres/src/main/java/org/citydb/database/postgres/SchemaAdapter.java index 7cb2d581..6a63fd42 100644 --- a/citydb-database-postgres/src/main/java/org/citydb/database/postgres/SchemaAdapter.java +++ b/citydb-database-postgres/src/main/java/org/citydb/database/postgres/SchemaAdapter.java @@ -89,18 +89,7 @@ public int getMaximumNumberOfItemsForInOperator() { @Override public String getFeatureHierarchyQuery() { try { - return PlainText.of(featureHierarchyQuery.get(), - "F.ENVELOPE").toString(); - } catch (Exception e) { - throw new IllegalStateException("Failed to create feature hierarchy query.", e); - } - } - - @Override - public String getFeatureHierarchyQuery(int targetSRID) { - try { - return PlainText.of(featureHierarchyQuery.get(), - "st_transform(F.ENVELOPE, " + targetSRID + ")").toString(); + return featureHierarchyQuery.get(); } catch (Exception e) { throw new IllegalStateException("Failed to create feature hierarchy query.", e); } diff --git a/citydb-database-postgres/src/main/resources/org/citydb/database/postgres/query_feature_hierarchy.sql b/citydb-database-postgres/src/main/resources/org/citydb/database/postgres/query_feature_hierarchy.sql index 09614960..0b661c83 100644 --- a/citydb-database-postgres/src/main/resources/org/citydb/database/postgres/query_feature_hierarchy.sql +++ b/citydb-database-postgres/src/main/resources/org/citydb/database/postgres/query_feature_hierarchy.sql @@ -80,19 +80,5 @@ SELECT H.VAL_FEATURE_ID, H.VAL_RELATION_TYPE, H.VAL_CONTENT, - H.VAL_CONTENT_MIME_TYPE, - F.OBJECTCLASS_ID, - F.OBJECTID, - F.IDENTIFIER, - F.IDENTIFIER_CODESPACE, - {} AS ENVELOPE, - F.LAST_MODIFICATION_DATE, - F.UPDATING_PERSON, - F.REASON_FOR_UPDATE, - F.LINEAGE, - F.CREATION_DATE, - F.TERMINATION_DATE, - F.VALID_FROM, - F.VALID_TO + H.VAL_CONTENT_MIME_TYPE FROM FEATURE_HIERARCHY H -LEFT JOIN @SCHEMA@.FEATURE F ON F.ID = H.VAL_FEATURE_ID diff --git a/citydb-database/src/main/java/org/citydb/database/adapter/SchemaAdapter.java b/citydb-database/src/main/java/org/citydb/database/adapter/SchemaAdapter.java index e3ce9d15..1462b457 100644 --- a/citydb-database/src/main/java/org/citydb/database/adapter/SchemaAdapter.java +++ b/citydb-database/src/main/java/org/citydb/database/adapter/SchemaAdapter.java @@ -57,8 +57,6 @@ protected SchemaAdapter(DatabaseAdapter adapter) { public abstract String getFeatureHierarchyQuery(); - public abstract String getFeatureHierarchyQuery(int targetSRID); - public abstract SqlObject getRecursiveImplicitGeometryQuery(Select featureQuery); public abstract Select getRecursiveLodQuery(Set lods, boolean requireAll, int searchDepth, Table table); diff --git a/citydb-operation/src/main/java/org/citydb/operation/exporter/ExportHelper.java b/citydb-operation/src/main/java/org/citydb/operation/exporter/ExportHelper.java index 1aa0337f..13994202 100644 --- a/citydb-operation/src/main/java/org/citydb/operation/exporter/ExportHelper.java +++ b/citydb-operation/src/main/java/org/citydb/operation/exporter/ExportHelper.java @@ -32,7 +32,7 @@ import org.citydb.model.common.Referencable; import org.citydb.model.feature.Feature; import org.citydb.model.geometry.ImplicitGeometry; -import org.citydb.operation.exporter.feature.FeatureExporter; +import org.citydb.operation.exporter.feature.FeatureHierarchyExporter; import org.citydb.operation.exporter.geometry.ImplicitGeometryExporter; import org.citydb.operation.exporter.options.LodOptions; import org.citydb.operation.exporter.util.LodFilter; @@ -166,7 +166,7 @@ public Selection getTransformOperator(Column column) { Feature exportFeature(long id, long sequenceId) throws ExportException { try { - Feature feature = tableHelper.getOrCreateExporter(FeatureExporter.class).doExport(id); + Feature feature = tableHelper.getOrCreateExporter(FeatureHierarchyExporter.class).doExport(id); feature.getDescriptor().ifPresent(descriptor -> descriptor.setSequenceId(sequenceId)); postprocessor.process(feature); return feature; diff --git a/citydb-operation/src/main/java/org/citydb/operation/exporter/feature/FeatureExporter.java b/citydb-operation/src/main/java/org/citydb/operation/exporter/feature/FeatureExporter.java index c6a2b485..9083c451 100644 --- a/citydb-operation/src/main/java/org/citydb/operation/exporter/feature/FeatureExporter.java +++ b/citydb-operation/src/main/java/org/citydb/operation/exporter/feature/FeatureExporter.java @@ -27,50 +27,90 @@ import org.citydb.operation.exporter.ExportException; import org.citydb.operation.exporter.ExportHelper; import org.citydb.operation.exporter.common.DatabaseExporter; -import org.citydb.operation.exporter.hierarchy.HierarchyBuilder; +import org.citydb.sqlbuilder.literal.Placeholder; +import org.citydb.sqlbuilder.query.Select; +import org.citydb.sqlbuilder.schema.Table; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.time.OffsetDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; public class FeatureExporter extends DatabaseExporter { + private final Table feature; + private final Select select; public FeatureExporter(ExportHelper helper) throws SQLException { super(helper); - stmt = helper.getConnection().prepareStatement(getQuery()); + feature = tableHelper.getTable(org.citydb.database.schema.Table.FEATURE); + select = getBaseQuery(); + stmt = helper.getConnection().prepareStatement(Select.of(select) + .where(feature.column("id").eq(Placeholder.empty())) + .toSql()); } - private String getQuery() { - return adapter.getDatabaseMetadata().getSpatialReference().getSRID() == helper.getSRID() ? - adapter.getSchemaAdapter().getFeatureHierarchyQuery() : - adapter.getSchemaAdapter().getFeatureHierarchyQuery(helper.getSRID()); + private Select getBaseQuery() { + return Select.newInstance() + .select(feature.columns("id", "objectclass_id", "objectid", "identifier", "identifier_codespace", + "last_modification_date", "updating_person", "reason_for_update", "lineage", + "creation_date", "termination_date", "valid_from", "valid_to")) + .select(helper.getTransformOperator(feature.column("envelope"))) + .from(feature); + } + + private Select getQuery(Set ids) { + return Select.of(select) + .where(operationHelper.in(feature.column("id"), ids)); } public Feature doExport(long id) throws ExportException, SQLException { stmt.setLong(1, id); try (ResultSet rs = stmt.executeQuery()) { - return HierarchyBuilder.newInstance(id, helper) - .initialize(rs) - .build() - .getFeature(id); + return doExport(rs).get(id); } } - public Feature doExport(long id, ResultSet rs) throws ExportException, SQLException { - FeatureType featureType = schemaMapping.getFeatureType(rs.getInt("objectclass_id")); - return Feature.of(featureType.getName()) - .setObjectId(rs.getString("objectid")) - .setIdentifier(rs.getString("identifier")) - .setIdentifierCodeSpace(rs.getString("identifier_codespace")) - .setEnvelope(getEnvelope(rs.getObject("envelope"))) - .setLastModificationDate(rs.getObject("last_modification_date", OffsetDateTime.class)) - .setUpdatingPerson(rs.getString("updating_person")) - .setReasonForUpdate(rs.getString("reason_for_update")) - .setLineage(rs.getString("lineage")) - .setCreationDate(rs.getObject("creation_date", OffsetDateTime.class)) - .setTerminationDate(rs.getObject("termination_date", OffsetDateTime.class)) - .setValidFrom(rs.getObject("valid_from", OffsetDateTime.class)) - .setValidTo(rs.getObject("valid_to", OffsetDateTime.class)) - .setDescriptor(FeatureDescriptor.of(id, featureType.getId())); + public Map doExport(Set ids) throws ExportException, SQLException { + if (ids.size() == 1) { + stmt.setLong(1, ids.iterator().next()); + try (ResultSet rs = stmt.executeQuery()) { + return doExport(rs); + } + } else if (!ids.isEmpty()) { + try (Statement stmt = helper.getConnection().createStatement(); + ResultSet rs = stmt.executeQuery(getQuery(ids).toSql())) { + return doExport(rs); + } + } else { + return Collections.emptyMap(); + } + } + + private Map doExport(ResultSet rs) throws ExportException, SQLException { + Map features = new HashMap<>(); + while (rs.next()) { + long id = rs.getLong("id"); + FeatureType featureType = schemaMapping.getFeatureType(rs.getInt("objectclass_id")); + features.put(id, Feature.of(featureType.getName()) + .setObjectId(rs.getString("objectid")) + .setIdentifier(rs.getString("identifier")) + .setIdentifierCodeSpace(rs.getString("identifier_codespace")) + .setEnvelope(getEnvelope(rs.getObject("envelope"))) + .setLastModificationDate(rs.getObject("last_modification_date", OffsetDateTime.class)) + .setUpdatingPerson(rs.getString("updating_person")) + .setReasonForUpdate(rs.getString("reason_for_update")) + .setLineage(rs.getString("lineage")) + .setCreationDate(rs.getObject("creation_date", OffsetDateTime.class)) + .setTerminationDate(rs.getObject("termination_date", OffsetDateTime.class)) + .setValidFrom(rs.getObject("valid_from", OffsetDateTime.class)) + .setValidTo(rs.getObject("valid_to", OffsetDateTime.class)) + .setDescriptor(FeatureDescriptor.of(id, featureType.getId()))); + } + + return features; } } diff --git a/citydb-operation/src/main/java/org/citydb/operation/exporter/feature/FeatureHierarchyExporter.java b/citydb-operation/src/main/java/org/citydb/operation/exporter/feature/FeatureHierarchyExporter.java new file mode 100644 index 00000000..1f0811e4 --- /dev/null +++ b/citydb-operation/src/main/java/org/citydb/operation/exporter/feature/FeatureHierarchyExporter.java @@ -0,0 +1,49 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citydb.operation.exporter.feature; + +import org.citydb.model.feature.Feature; +import org.citydb.operation.exporter.ExportException; +import org.citydb.operation.exporter.ExportHelper; +import org.citydb.operation.exporter.common.DatabaseExporter; +import org.citydb.operation.exporter.hierarchy.HierarchyBuilder; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class FeatureHierarchyExporter extends DatabaseExporter { + + public FeatureHierarchyExporter(ExportHelper helper) throws SQLException { + super(helper); + stmt = helper.getConnection().prepareStatement(adapter.getSchemaAdapter().getFeatureHierarchyQuery()); + } + + public Feature doExport(long id) throws ExportException, SQLException { + stmt.setLong(1, id); + try (ResultSet rs = stmt.executeQuery()) { + return HierarchyBuilder.newInstance(id, helper) + .initialize(rs) + .build() + .getFeature(id); + } + } +} diff --git a/citydb-operation/src/main/java/org/citydb/operation/exporter/hierarchy/HierarchyBuilder.java b/citydb-operation/src/main/java/org/citydb/operation/exporter/hierarchy/HierarchyBuilder.java index 045b9687..04fd5a10 100644 --- a/citydb-operation/src/main/java/org/citydb/operation/exporter/hierarchy/HierarchyBuilder.java +++ b/citydb-operation/src/main/java/org/citydb/operation/exporter/hierarchy/HierarchyBuilder.java @@ -68,6 +68,7 @@ public static HierarchyBuilder newInstance(long rootId, ExportHelper helper) { } public HierarchyBuilder initialize(ResultSet rs) throws ExportException, SQLException { + Set nestedFeatureIds = new HashSet<>(); Set geometryIds = new HashSet<>(); Set appearanceIds = new HashSet<>(); Set addressIds = new HashSet<>(); @@ -76,8 +77,7 @@ public HierarchyBuilder initialize(ResultSet rs) throws ExportException, SQLExce while (rs.next()) { long nestedFeatureId = rs.getLong("val_feature_id"); if (!rs.wasNull() && hierarchy.getFeature(nestedFeatureId) == null) { - hierarchy.addFeature(nestedFeatureId, tableHelper.getOrCreateExporter(FeatureExporter.class) - .doExport(nestedFeatureId, rs)); + nestedFeatureIds.add(nestedFeatureId); } long geometryId = rs.getLong("val_geometry_id"); @@ -114,6 +114,12 @@ public HierarchyBuilder initialize(ResultSet rs) throws ExportException, SQLExce } } + if (!nestedFeatureIds.isEmpty()) { + tableHelper.getOrCreateExporter(FeatureExporter.class) + .doExport(nestedFeatureIds) + .forEach(hierarchy::addFeature); + } + if (!geometryIds.isEmpty()) { tableHelper.getOrCreateExporter(GeometryExporter.class) .doExport(geometryIds, false) diff --git a/citydb-operation/src/main/java/org/citydb/operation/exporter/util/TableHelper.java b/citydb-operation/src/main/java/org/citydb/operation/exporter/util/TableHelper.java index 1225be26..1002e765 100644 --- a/citydb-operation/src/main/java/org/citydb/operation/exporter/util/TableHelper.java +++ b/citydb-operation/src/main/java/org/citydb/operation/exporter/util/TableHelper.java @@ -26,6 +26,7 @@ import org.citydb.operation.exporter.address.AddressExporter; import org.citydb.operation.exporter.appearance.*; import org.citydb.operation.exporter.common.DatabaseExporter; +import org.citydb.operation.exporter.feature.FeatureHierarchyExporter; import org.citydb.operation.exporter.feature.FeatureExporter; import org.citydb.operation.exporter.geometry.GeometryExporter; import org.citydb.operation.exporter.geometry.ImplicitGeometryExporter; @@ -52,7 +53,9 @@ public T getOrCreateExporter(Class type) throws DatabaseExporter exporter = exporters.get(type.getName()); if (exporter == null) { try { - if (type == FeatureExporter.class) { + if (type == FeatureHierarchyExporter.class) { + exporter = new FeatureHierarchyExporter(helper); + } else if (type == FeatureExporter.class) { exporter = new FeatureExporter(helper); } else if (type == GeometryExporter.class) { exporter = new GeometryExporter(helper);