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