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