001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/datastore/sde/SDEInsertHandler.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2006 by: M.O.S.S. Computer Grafik Systeme GmbH 006 Hohenbrunner Weg 13 007 D-82024 Taufkirchen 008 http://www.moss.de/ 009 010 This library is free software; you can redistribute it and/or 011 modify it under the terms of the GNU Lesser General Public 012 License as published by the Free Software Foundation; either 013 version 2.1 of the License, or (at your option) any later version. 014 015 This library is distributed in the hope that it will be useful, 016 but WITHOUT ANY WARRANTY; without even the implied warranty of 017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 018 Lesser General Public License for more details. 019 020 You should have received a copy of the GNU Lesser General Public 021 License along with this library; if not, write to the Free Software 022 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 023 024 ---------------------------------------------------------------------------*/ 025 package org.deegree.io.datastore.sde; 026 027 import java.util.ArrayList; 028 import java.util.Collection; 029 import java.util.HashMap; 030 import java.util.Iterator; 031 import java.util.List; 032 import java.util.Map; 033 034 import org.deegree.datatypes.Types; 035 import org.deegree.framework.log.ILogger; 036 import org.deegree.framework.log.LoggerFactory; 037 import org.deegree.framework.util.StringTools; 038 import org.deegree.io.datastore.DatastoreException; 039 import org.deegree.io.datastore.FeatureId; 040 import org.deegree.io.datastore.TransactionException; 041 import org.deegree.io.datastore.idgenerator.FeatureIdAssigner; 042 import org.deegree.io.datastore.idgenerator.IdGenerationException; 043 import org.deegree.io.datastore.idgenerator.ParentIDGenerator; 044 import org.deegree.io.datastore.schema.MappedFeaturePropertyType; 045 import org.deegree.io.datastore.schema.MappedFeatureType; 046 import org.deegree.io.datastore.schema.MappedGeometryPropertyType; 047 import org.deegree.io.datastore.schema.MappedPropertyType; 048 import org.deegree.io.datastore.schema.MappedSimplePropertyType; 049 import org.deegree.io.datastore.schema.TableRelation; 050 import org.deegree.io.datastore.schema.content.MappingField; 051 import org.deegree.io.datastore.schema.content.SimpleContent; 052 import org.deegree.io.datastore.sql.transaction.insert.FeatureRow; 053 import org.deegree.io.datastore.sql.transaction.insert.InsertField; 054 import org.deegree.io.datastore.sql.transaction.insert.InsertRow; 055 import org.deegree.io.sdeapi.SDEAdapter; 056 import org.deegree.model.feature.Feature; 057 import org.deegree.model.feature.FeatureProperty; 058 import org.deegree.model.feature.schema.FeaturePropertyType; 059 import org.deegree.model.feature.schema.GeometryPropertyType; 060 import org.deegree.model.feature.schema.SimplePropertyType; 061 import org.deegree.model.spatialschema.Geometry; 062 063 import com.esri.sde.sdk.client.SeException; 064 import com.esri.sde.sdk.client.SeInsert; 065 import com.esri.sde.sdk.client.SeObjectId; 066 import com.esri.sde.sdk.client.SeQuery; 067 import com.esri.sde.sdk.client.SeRow; 068 import com.esri.sde.sdk.client.SeSqlConstruct; 069 import com.esri.sde.sdk.client.SeState; 070 071 /** 072 * Handler for <code>Insert</code> operations (usually contained in <code>Transaction</code> requests). 073 * 074 * @author <a href="mailto:cpollmann@moss.de">Christoph Pollmann</a> 075 * @author last edited by: $Author: aschmitz $ 076 * 077 * @version $Revision: 23585 $ 078 */ 079 public class SDEInsertHandler extends AbstractSDERequestHandler { 080 081 private static final ILogger LOG = LoggerFactory.getLogger( SDEInsertHandler.class ); 082 083 // features that are currently being processed 084 private Map<FeatureId, FeatureRow> featuresInInsertion = new HashMap<FeatureId, FeatureRow>(); 085 086 // contains only property rows and join table rows (but no feature rows) 087 private List<InsertRow> insertRows = new ArrayList<InsertRow>(); 088 089 private SDETransaction dsTa; 090 091 /** 092 * Creates a new <code>InsertHandler</code> from the given parameters. 093 * 094 * @param dsTa 095 */ 096 public SDEInsertHandler( SDETransaction dsTa ) { 097 super( dsTa.getDatastore(), dsTa.getAliasGenerator(), dsTa.getConnection() ); 098 this.dsTa = dsTa; 099 } 100 101 /** 102 * Inserts the given feature instance into the datastore. 103 * 104 * @param features 105 * (which have a MappedFeatureType as feature type) 106 * @return feature ids of inserted (root) feature instances 107 * @throws DatastoreException 108 * if the insert could not be performed 109 */ 110 public List<FeatureId> performInsert( List<Feature> features ) 111 throws DatastoreException { 112 113 List<FeatureId> fids = new ArrayList<FeatureId>(); 114 115 for ( int i = 0; i < features.size(); i++ ) { 116 Feature feature = features.get( i ); 117 MappedFeatureType ft = (MappedFeatureType) feature.getFeatureType(); 118 if ( feature.getId().startsWith( FeatureIdAssigner.EXISTS_MARKER ) ) { 119 String msg = "feature already exists " + feature.getName() + " id=" + feature.getId().substring( 1 ); 120 throw new TransactionException( msg ); 121 } 122 LOG.logDebug( "Inserting root feature '" + feature.getId() + "'..." ); 123 insertFeature( feature ); 124 FeatureId fid = new FeatureId( ft, feature.getId() ); 125 fids.add( fid ); 126 } 127 128 // merge inserts rows that are identical (except their pks) 129 this.insertRows = mergeInsertRows( this.insertRows ); 130 131 // add featureRows to insertRows 132 Iterator<FeatureRow> iter = this.featuresInInsertion.values().iterator(); 133 while ( iter.hasNext() ) { 134 this.insertRows.add( iter.next() ); 135 } 136 137 // try to sort the insert rows topologically (but continue in original order, if not topological order is 138 // possible) 139 List<InsertRow> sortedInserts = InsertRow.getInsertOrder( this.insertRows ); 140 141 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 142 Iterator<InsertRow> iter2 = sortedInserts.iterator(); 143 LOG.logDebug( sortedInserts.size() + " rows to be inserted: " ); 144 while ( iter2.hasNext() ) { 145 LOG.logDebug( iter2.next().toString() ); 146 } 147 } 148 149 executeInserts( sortedInserts ); 150 151 return fids; 152 } 153 154 /** 155 * Builds the <code>InsertRows</code> that are necessary to insert the given feature instance (including all 156 * properties + subfeatures). 157 * 158 * @param feature 159 * @return the row of the given feature 160 * @throws TransactionException 161 */ 162 private FeatureRow insertFeature( Feature feature ) 163 throws TransactionException { 164 165 MappedFeatureType ft = (MappedFeatureType) feature.getFeatureType(); 166 if ( !ft.isInsertable() ) { 167 String msg = "featuretype can't be inserted " + ft.getName(); 168 throw new TransactionException( msg ); 169 } 170 171 LOG.logDebug( "Creating InsertRow for feature with type '" + ft.getName() + "' and id: '" + feature.getId() 172 + "'." ); 173 174 // extract feature id column value 175 MappingField[] fidFields = ft.getGMLId().getIdFields(); 176 if ( fidFields.length > 1 ) { 177 throw new TransactionException( "Insertion of features with compound feature ids is not " + "supported." ); 178 } 179 Object fidValue = null; 180 try { 181 fidValue = FeatureId.removeFIDPrefix( feature.getId(), ft.getGMLId() ); 182 } catch ( DatastoreException e ) { 183 e.printStackTrace(); 184 throw new TransactionException( e.getMessage(), e ); 185 } 186 FeatureId fid = new FeatureId( ft, new Object[] { fidValue } ); 187 188 // check if the feature id is already being inserted (happens for cyclic features) 189 FeatureRow insertRow = this.featuresInInsertion.get( fid ); 190 if ( insertRow != null ) { 191 return insertRow; 192 } 193 194 insertRow = new FeatureRow( ft.getTable() ); 195 this.featuresInInsertion.put( fid, insertRow ); 196 197 // add column value for fid (primary key) 198 String fidColumn = fidFields[0].getField(); 199 insertRow.setColumn( fidColumn, fidValue, ft.getGMLId().getIdFields()[0].getType(), true ); 200 201 // process properties 202 FeatureProperty[] properties = feature.getProperties(); 203 for ( int i = 0; i < properties.length; i++ ) { 204 FeatureProperty property = properties[i]; 205 MappedPropertyType propertyType = (MappedPropertyType) ft.getProperty( property.getName() ); 206 if ( propertyType == null ) { 207 String msg = "Unknown propertytype " + property.getName(); 208 LOG.logDebug( msg ); 209 throw new TransactionException( msg ); 210 } 211 insertProperty( property, propertyType, insertRow ); 212 } 213 return insertRow; 214 } 215 216 /** 217 * Builds the <code>InsertRow</code>s that are necessary to insert the given property instance (including all it's 218 * subfeatures). 219 * 220 * @param property 221 * property instance to be inserted 222 * @param propertyType 223 * property type of the property 224 * @param featureRow 225 * table row of the parent feature instance 226 * @throws TransactionException 227 */ 228 private void insertProperty( FeatureProperty property, MappedPropertyType propertyType, InsertRow featureRow ) 229 throws TransactionException { 230 231 if ( propertyType instanceof SimplePropertyType ) { 232 String msg = StringTools.concat( 300, "- Simple property '", propertyType.getName(), 233 "', value='" + getPropertyValue( property ), "'." ); 234 LOG.logDebug( msg ); 235 insertProperty( (MappedSimplePropertyType) propertyType, property, featureRow ); 236 } else if ( propertyType instanceof GeometryPropertyType ) { 237 LOG.logDebug( "- Geometry property: '" + propertyType.getName() + "'" ); 238 insertProperty( (MappedGeometryPropertyType) propertyType, property, featureRow ); 239 } else if ( propertyType instanceof FeaturePropertyType ) { 240 LOG.logDebug( "- Feature property: '" + propertyType.getName() + "'" ); 241 insertProperty( (MappedFeaturePropertyType) propertyType, property, featureRow ); 242 } else { 243 throw new TransactionException( "Unhandled property type '" + propertyType.getClass().getName() + "'." ); 244 } 245 } 246 247 /** 248 * Inserts the given simple property (stored in feature table or in related table). 249 * 250 * @param pt 251 * @param property 252 * @param featureRow 253 * @throws TransactionException 254 */ 255 private void insertProperty( MappedSimplePropertyType pt, FeatureProperty property, InsertRow featureRow ) 256 throws TransactionException { 257 258 SimpleContent content = pt.getContent(); 259 if ( content.isUpdateable() ) { 260 if ( content instanceof MappingField ) { 261 MappingField mf = (MappingField) content; 262 String propertyColumn = mf.getField(); 263 Object propertyValue = property.getValue(); 264 int propertyType = mf.getType(); 265 TableRelation[] relations = pt.getTableRelations(); 266 insertProperty( propertyColumn, propertyValue, propertyType, relations, featureRow ); 267 } 268 } 269 } 270 271 /** 272 * Inserts the given geometry property (stored in feature table or in related table). 273 * 274 * @param pt 275 * @param property 276 * @param featureRow 277 * @throws TransactionException 278 */ 279 private void insertProperty( MappedGeometryPropertyType pt, FeatureProperty property, InsertRow featureRow ) 280 throws TransactionException { 281 282 String propertyColumn = pt.getMappingField().getField(); 283 284 Geometry deegreeGeometry = (Geometry) property.getValue(); 285 Object dbGeometry; 286 287 try { 288 dbGeometry = this.datastore.convertDegreeToDBGeometry( deegreeGeometry ); 289 } catch ( DatastoreException e ) { 290 throw new TransactionException( e.getMessage(), e ); 291 } 292 293 int propertyType = pt.getMappingField().getType(); 294 295 TableRelation[] relations = pt.getTableRelations(); 296 insertProperty( propertyColumn, dbGeometry, propertyType, relations, featureRow ); 297 } 298 299 /** 300 * Inserts the given simple / geometry property (stored in feature table or in related table). 301 * 302 * @param propertyColumn 303 * @param propertyValue 304 * @param propertyType 305 * @param featureRow 306 * @throws TransactionException 307 */ 308 private void insertProperty( String propertyColumn, Object propertyValue, int propertyType, 309 TableRelation[] relations, InsertRow featureRow ) 310 throws TransactionException { 311 312 if ( relations == null || relations.length == 0 ) { 313 // property is stored in feature table 314 featureRow.setColumn( propertyColumn, propertyValue, propertyType, false ); 315 } else { 316 // property is stored in related table 317 if ( relations.length > 1 ) { 318 throw new TransactionException( "properties in related tables are not allowed here" ); 319 } 320 321 if ( !relations[0].isFromFK() ) { 322 // fk is in property table 323 MappingField[] pkFields = relations[0].getFromFields(); 324 MappingField[] fkFields = relations[0].getToFields(); 325 326 for ( int i = 0; i < pkFields.length; i++ ) { 327 InsertField pkField = featureRow.getColumn( pkFields[i].getField() ); 328 if ( pkField == null ) { 329 String msg = "Missing foreign key " + pkFields[i].getField() + " / " + pkFields[i].getTable(); 330 throw new TransactionException( msg ); 331 } 332 int pkColumnType = pkField.getSQLType(); 333 int fkColumnType = fkFields[i].getType(); 334 if ( pkColumnType != fkColumnType ) { 335 String msg = "FK_PK_TYPE_MISMATCH"; 336 throw new TransactionException( msg ); 337 } 338 InsertRow insertRow = new InsertRow( relations[0].getToTable() ); 339 insertRow.linkColumn( fkFields[i].getField(), pkField ); 340 insertRow.setColumn( propertyColumn, propertyValue, propertyType, false ); 341 this.insertRows.add( insertRow ); 342 } 343 } else { 344 // fk is in feature table 345 MappingField[] pkFields = relations[0].getToFields(); 346 MappingField[] fkFields = relations[0].getFromFields(); 347 348 // generate necessary primary key value 349 InsertField pkField = null; 350 try { 351 Object pk = null; 352 // TODO remove hack!!! 353 if ( relations[0].getIdGenerator() instanceof ParentIDGenerator ) { 354 InsertField field = featureRow.getColumn( "ID" ); 355 if ( field == null ) { 356 throw new TransactionException( "No value for ID available!" ); 357 } 358 pk = field.getValue(); 359 } else { 360 pk = relations[0].getNewPK( this.dsTa ); 361 } 362 InsertRow insertRow = findOrCreateRow( relations[0].getToTable(), pkFields[0].getField(), pk ); 363 pkField = insertRow.setColumn( pkFields[0].getField(), pk, pkFields[0].getType(), true ); 364 insertRow.setColumn( propertyColumn, propertyValue, propertyType, false ); 365 } catch ( IdGenerationException e ) { 366 throw new TransactionException( e.getMessage(), e ); 367 } 368 featureRow.linkColumn( fkFields[0].getField(), pkField ); 369 } 370 } 371 } 372 373 /** 374 * Inserts the given feature property. 375 * 376 * @param pt 377 * @param property 378 * @param featureRow 379 * @throws TransactionException 380 */ 381 private void insertProperty( MappedFeaturePropertyType pt, FeatureProperty property, InsertRow featureRow ) 382 throws TransactionException { 383 384 // find (concrete) subfeature type for the given property instance 385 MappedFeatureType propertyFeatureType = pt.getFeatureTypeReference().getFeatureType(); 386 MappedFeatureType[] substitutions = propertyFeatureType.getConcreteSubstitutions(); 387 Feature subFeature = (Feature) property.getValue(); 388 MappedFeatureType subFeatureType = null; 389 for ( int i = 0; i < substitutions.length; i++ ) { 390 if ( substitutions[i].getName().equals( subFeature.getName() ) ) { 391 subFeatureType = substitutions[i]; 392 break; 393 } 394 } 395 if ( subFeatureType == null ) { 396 String msg = "ERROR_FEATURE_NOT_SUBSTITUTABLE " + propertyFeatureType.getName() + "->" 397 + subFeature.getName(); 398 throw new TransactionException( msg ); 399 } 400 boolean ftIsAbstract = propertyFeatureType.isAbstract(); 401 402 TableRelation[] relations = pt.getTableRelations(); 403 if ( relations == null || relations.length < 1 ) { 404 throw new TransactionException( "Invalid feature property definition, feature property " 405 + "mappings must use at least one 'TableRelation' element." ); 406 } 407 408 // workaround for links to dummy InsertRows (of already stored features) 409 boolean cutLink = subFeature.getId().startsWith( FeatureIdAssigner.EXISTS_MARKER ); 410 InsertRow subFeatureRow = null; 411 if ( cutLink ) { 412 try { 413 Object fidValue = FeatureId.removeFIDPrefix( subFeature.getId().substring( 1 ), 414 subFeatureType.getGMLId() ); 415 subFeatureRow = new FeatureRow( subFeatureType.getTable() ); 416 // add column value for fid (primary key) 417 String fidColumn = subFeatureType.getGMLId().getIdFields()[0].getField(); 418 subFeatureRow.setColumn( fidColumn, fidValue, subFeatureType.getGMLId().getIdFields()[0].getType(), 419 true ); 420 } catch ( DatastoreException e ) { 421 throw new TransactionException( e ); 422 } 423 } else { 424 // insert sub feature (if it is not already stored) 425 subFeatureRow = insertFeature( subFeature ); 426 } 427 428 if ( relations.length == 1 ) { 429 430 if ( relations[0].isFromFK() ) { 431 // fk is in feature table 432 MappingField[] pkFields = relations[0].getToFields(); 433 MappingField[] fkFields = relations[0].getFromFields(); 434 435 for ( int i = 0; i < pkFields.length; i++ ) { 436 InsertField pkField = subFeatureRow.getColumn( pkFields[i].getField() ); 437 if ( pkField == null ) { 438 String msg = "Missing foreign key."; 439 throw new TransactionException( msg ); 440 } 441 int pkColumnType = pkField.getSQLType(); 442 int fkColumnType = fkFields[i].getType(); 443 if ( pkColumnType != fkColumnType ) { 444 String msg = "ERROR_FK_PK_TYPE_MISMATCH"; 445 throw new TransactionException( msg ); 446 } 447 448 if ( !cutLink ) { 449 featureRow.linkColumn( fkFields[i].getField(), pkField ); 450 } else { 451 featureRow.setColumn( fkFields[i].getField(), pkField.getValue(), pkField.getSQLType(), false ); 452 } 453 454 } 455 456 if ( ftIsAbstract ) { 457 String typeField = FT_PREFIX + relations[0].getToTable(); 458 featureRow.setColumn( typeField, subFeatureType.getName().getLocalName(), Types.VARCHAR, false ); 459 } 460 } else { 461 // fk is in subfeature table 462 MappingField[] pkFields = relations[0].getFromFields(); 463 MappingField[] fkFields = relations[0].getToFields(); 464 465 InsertField pkField = featureRow.getColumn( pkFields[0].getField() ); 466 467 if ( pkField == null ) { 468 String msg = "Missing foreign key."; 469 throw new TransactionException( msg ); 470 } 471 int pkColumnType = pkField.getSQLType(); 472 int fkColumnType = fkFields[0].getType(); 473 if ( pkColumnType != fkColumnType ) { 474 String msg = "ERROR_FK_PK_TYPE_MISMATCH"; 475 throw new TransactionException( msg ); 476 } 477 478 if ( !cutLink ) { 479 subFeatureRow.linkColumn( fkFields[0].getField(), pkField ); 480 } else { 481 featureRow.setColumn( fkFields[0].getField(), pkField.getValue(), pkField.getSQLType(), false ); 482 } 483 } 484 } else if ( relations.length == 2 ) { 485 486 // insert into join table 487 String joinTable = relations[0].getToTable(); 488 MappingField[] leftKeyFields = relations[0].getToFields(); 489 MappingField[] rightKeyFields = relations[1].getFromFields(); 490 491 InsertRow jtRow = new InsertRow( joinTable ); 492 if ( ftIsAbstract ) { 493 jtRow.setColumn( FT_COLUMN, subFeatureType.getName().getLocalName(), Types.VARCHAR, false ); 494 } 495 496 if ( !relations[0].isFromFK() ) { 497 // left key field in join table is fk 498 MappingField[] pkFields = relations[0].getFromFields(); 499 InsertField pkField = featureRow.getColumn( pkFields[0].getField() ); 500 if ( pkField == null ) { 501 throw new TransactionException( "Insertion of feature property using join table failed: " 502 + "no value for join table key column." ); 503 } 504 jtRow.linkColumn( leftKeyFields[0].getField(), pkField ); 505 } else { 506 // left key field in join table is pk 507 MappingField[] pkFields = relations[0].getToFields(); 508 // generate necessary primary key value 509 InsertField pkField = null; 510 try { 511 Object pk = relations[0].getNewPK( this.dsTa ); 512 pkField = jtRow.setColumn( pkFields[0].getField(), pk, pkFields[0].getType(), true ); 513 } catch ( IdGenerationException e ) { 514 throw new TransactionException( e.getMessage(), e ); 515 } 516 featureRow.linkColumn( relations[0].getFromFields()[0].getField(), pkField ); 517 } 518 519 if ( relations[1].isFromFK() ) { 520 // right key field in join table is fk 521 MappingField[] pkFields = relations[1].getToFields(); 522 InsertField pkField = subFeatureRow.getColumn( pkFields[0].getField() ); 523 if ( pkField == null ) { 524 throw new TransactionException( "Insertion of feature property using join table failed: " 525 + "no value for join table key column." ); 526 } 527 if ( !cutLink ) { 528 jtRow.linkColumn( rightKeyFields[0].getField(), pkField ); 529 } else { 530 jtRow.setColumn( rightKeyFields[0].getField(), pkField.getValue(), pkField.getSQLType(), false ); 531 } 532 } else { 533 // right key field in join table is pk 534 MappingField[] pkFields = relations[1].getFromFields(); 535 // generate necessary primary key value 536 InsertField pkField = null; 537 try { 538 Object pk = relations[1].getNewPK( this.dsTa ); 539 pkField = jtRow.setColumn( pkFields[0].getField(), pk, pkFields[0].getType(), true ); 540 } catch ( IdGenerationException e ) { 541 throw new TransactionException( e.getMessage(), e ); 542 } 543 if ( !cutLink ) { 544 subFeatureRow.linkColumn( relations[1].getToFields()[0].getField(), pkField ); 545 } 546 } 547 548 this.insertRows.add( jtRow ); 549 } else { 550 throw new TransactionException( "Insertion of feature properties stored in related tables " 551 + "connected via more than one join table is not supported." ); 552 } 553 } 554 555 /** 556 * Checks whether the feature that corresponds to the given FeatureRow is already stored in the database. 557 * 558 * @param featureRow 559 * @return true if the feature exists 560 * @throws TransactionException 561 */ 562 private boolean doesFeatureExist( FeatureRow featureRow ) 563 throws TransactionException { 564 565 boolean exists = false; 566 567 InsertField pkField = featureRow.getPKColumn(); 568 569 SeQuery stmt = null; 570 try { 571 stmt = buildFeatureSelect( pkField.getColumnName(), pkField.getValue(), featureRow.getTable() ); 572 stmt.execute(); 573 SeRow row = stmt.fetch(); 574 if ( null != row ) { 575 exists = true; 576 } 577 row = stmt.fetch(); 578 if ( null != row ) { 579 String msg = "ERROR_FEATURE_QUERY_MORE_THAN_ONE_RESULT"; 580 LOG.logError( msg ); 581 throw new TransactionException( msg ); 582 } 583 } catch ( Exception e ) { 584 throw new TransactionException( e ); 585 } finally { 586 try { 587 stmt.close(); 588 } catch ( Exception e ) { 589 LOG.logDebug( "Error in SDE command", e ); 590 } 591 } 592 return exists; 593 } 594 595 /** 596 * Builds a SELECT statement that checks for the existence of a feature with the given id. 597 * 598 * @param fidColumn 599 * @param fidValue 600 * @param table 601 * @return the statement 602 */ 603 private SeQuery buildFeatureSelect( String fidColumn, Object fidValue, String table ) { 604 SeQuery query = null; 605 try { 606 SeSqlConstruct constr = new SeSqlConstruct( table, fidColumn + "='" + fidValue.toString() + "'" ); 607 String[] columns = new String[1]; 608 columns[0] = fidColumn; 609 query = new SeQuery( getConnection().getConnection(), columns, constr ); 610 query.setState( getConnection().getState().getId(), new SeObjectId( SeState.SE_NULL_STATE_ID ), 611 SeState.SE_STATE_DIFF_NOCHECK ); 612 query.prepareQuery(); 613 } catch ( Exception e ) { 614 LOG.logError( "Error building featureSelect", e ); 615 } 616 return query; 617 } 618 619 private InsertRow findOrCreateRow( String table, String pkColumn, Object value ) { 620 Iterator<InsertRow> rowIter = this.insertRows.iterator(); 621 boolean found = false; 622 InsertRow row = null; 623 while ( rowIter.hasNext() ) { 624 row = rowIter.next(); 625 if ( row.getTable().equals( table ) ) { 626 InsertField field = row.getColumn( pkColumn ); 627 if ( value.equals( field.getValue() ) ) { 628 found = true; 629 LOG.logDebug( "Found matching row " + row ); 630 break; 631 } 632 } 633 } 634 if ( !found ) { 635 row = new InsertRow( table ); 636 this.insertRows.add( row ); 637 } 638 return row; 639 } 640 641 private String getPropertyValue( FeatureProperty property ) { 642 Object value = property.getValue(); 643 StringBuffer sb = new StringBuffer(); 644 if ( value instanceof Object[] ) { 645 Object[] objects = (Object[]) value; 646 for ( int i = 0; i < objects.length; i++ ) { 647 sb.append( objects[i] ); 648 } 649 } else { 650 sb.append( value ); 651 } 652 return sb.toString(); 653 } 654 655 /** 656 * Transforms the given <code>List</code> of <code>InsertRows</code> into SQL INSERT statements and executes them 657 * using the underlying JDBC connection. 658 * 659 * @param inserts 660 * @throws TransactionException 661 * if an SQL error occurs 662 */ 663 private void executeInserts( List<InsertRow> inserts ) 664 throws TransactionException { 665 666 SeInsert stmt = null; 667 668 for ( InsertRow row : inserts ) { 669 if ( row instanceof FeatureRow ) { 670 if ( doesFeatureExist( (FeatureRow) row ) ) { 671 LOG.logDebug( "Skipping feature row. Already present in db." ); 672 continue; 673 } 674 } 675 try { 676 stmt = createStatement( row ); 677 stmt.execute(); 678 } catch ( Exception e ) { 679 String msg = "Error performing insert: " + e.getMessage(); 680 LOG.logError( msg, e ); 681 throw new TransactionException( msg, e ); 682 } finally { 683 if ( stmt != null ) { 684 try { 685 stmt.close(); 686 } catch ( Exception e ) { 687 String msg = "Error closing statement: " + e.getMessage(); 688 LOG.logError( msg, e ); 689 } 690 } 691 } 692 } 693 } 694 695 private SeInsert createStatement( InsertRow row ) 696 throws SeException { 697 SeInsert inserter = new SeInsert( conn.getConnection() ); 698 inserter.setState( conn.getState().getId(), new SeObjectId( SeState.SE_NULL_STATE_ID ), 699 SeState.SE_STATE_DIFF_NOCHECK ); 700 Collection<InsertField> fields = row.getColumns(); 701 String[] columns = new String[fields.size()]; 702 int i = 0; 703 for ( Iterator<InsertField> iter = fields.iterator(); iter.hasNext(); i++ ) { 704 InsertField field = iter.next(); 705 columns[i] = field.getColumnName(); 706 } 707 inserter.intoTable( row.getTable(), columns ); 708 SeRow insertRow = inserter.getRowToSet(); 709 for ( i = 0; i < columns.length; i++ ) { 710 InsertField field = row.getColumn( columns[i] ); 711 SDEAdapter.setRowValue( insertRow, i, field.getValue(), SDEAdapter.mapSQL2SDE( field.getSQLType() ) ); 712 } 713 return inserter; 714 } 715 716 /** 717 * Merges the given <code>InsertRow</code>s by eliminating rows that have identical content (except for their 718 * primary keys). 719 * <p> 720 * This only applies to non-FeatureRows: there are never two FeatureRows that may be treated as identical, because 721 * unique feature ids have been assigned to them before. 722 * 723 * @see FeatureIdAssigner 724 * 725 * @param insertRows 726 * @return the cleaned up list 727 */ 728 private List<InsertRow> mergeInsertRows( List<InsertRow> insertRows ) { 729 730 List<InsertRow> result = new ArrayList<InsertRow>(); 731 732 // keys: table names, values: inserts into the table 733 Map<String, Collection<InsertRow>> tableMap = new HashMap<String, Collection<InsertRow>>(); 734 735 // build table lookup map 736 Iterator<InsertRow> iter = insertRows.iterator(); 737 while ( iter.hasNext() ) { 738 InsertRow insertRow = iter.next(); 739 Collection<InsertRow> tableInserts = tableMap.get( insertRow.getTable() ); 740 if ( tableInserts == null ) { 741 tableInserts = new ArrayList<InsertRow>(); 742 tableMap.put( insertRow.getTable(), tableInserts ); 743 } 744 tableInserts.add( insertRow ); 745 } 746 747 iter = insertRows.iterator(); 748 while ( iter.hasNext() ) { 749 InsertRow insertRow = iter.next(); 750 boolean insert = true; 751 if ( !( insertRow instanceof FeatureRow ) ) { 752 Collection<InsertRow> tableInserts = tableMap.get( insertRow.getTable() ); 753 Iterator<InsertRow> candidatesIter = tableInserts.iterator(); 754 while ( candidatesIter.hasNext() ) { 755 InsertRow candidate = candidatesIter.next(); 756 if ( insertRow != candidate ) { 757 if ( compareInsertRows( insertRow, candidate ) ) { 758 LOG.logDebug( "Removing InsertRow: " + insertRow.hashCode() + " " + insertRow 759 + " - duplicate of: " + candidate ); 760 replaceInsertRow( insertRow, candidate ); 761 insert = false; 762 tableInserts.remove( insertRow ); 763 break; 764 } 765 } 766 } 767 } 768 if ( insert ) { 769 result.add( insertRow ); 770 } 771 } 772 return result; 773 } 774 775 private boolean compareInsertRows( InsertRow row1, InsertRow row2 ) { 776 Collection<InsertField> fields1 = row1.getColumns(); 777 Iterator<InsertField> iter = fields1.iterator(); 778 while ( iter.hasNext() ) { 779 InsertField field1 = iter.next(); 780 if ( !field1.isPK() ) { 781 InsertField field2 = row2.getColumn( field1.getColumnName() ); 782 Object value1 = field1.getValue(); 783 Object value2 = null; 784 if ( field2 != null ) 785 value2 = field2.getValue(); 786 if ( value1 == null ) { 787 if ( value2 == null ) { 788 continue; 789 } 790 return false; 791 } 792 if ( !value1.equals( value2 ) ) { 793 return false; 794 } 795 } 796 } 797 return true; 798 } 799 800 private void replaceInsertRow( InsertRow oldRow, InsertRow newRow ) { 801 802 Collection<InsertField> oldFields = oldRow.getColumns(); 803 for ( InsertField field : oldFields ) { 804 InsertField toField = field.getReferencedField(); 805 if ( toField != null ) { 806 LOG.logDebug( "Removing reference to field '" + toField + "'" ); 807 toField.removeReferencingField( field ); 808 } 809 } 810 811 Collection<InsertField> referencingFields = oldRow.getReferencingFields(); 812 for ( InsertField fromField : referencingFields ) { 813 LOG.logDebug( "Replacing reference for field '" + fromField + "'" ); 814 InsertField field = newRow.getColumn( fromField.getReferencedField().getColumnName() ); 815 LOG.logDebug( "" + field ); 816 fromField.relinkField( field ); 817 } 818 } 819 }