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 }