001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/datastore/sql/transaction/UpdateHandler.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstraße 19
030: 53177 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042: ---------------------------------------------------------------------------*/
043: package org.deegree.io.datastore.sql.transaction;
044:
045: import java.sql.Connection;
046: import java.sql.PreparedStatement;
047: import java.sql.ResultSet;
048: import java.sql.SQLException;
049: import java.util.ArrayList;
050: import java.util.List;
051: import java.util.Map;
052:
053: import org.deegree.datatypes.QualifiedName;
054: import org.deegree.datatypes.Types;
055: import org.deegree.framework.log.ILogger;
056: import org.deegree.framework.log.LoggerFactory;
057: import org.deegree.i18n.Messages;
058: import org.deegree.io.datastore.DatastoreException;
059: import org.deegree.io.datastore.FeatureId;
060: import org.deegree.io.datastore.idgenerator.FeatureIdAssigner;
061: import org.deegree.io.datastore.schema.MappedFeaturePropertyType;
062: import org.deegree.io.datastore.schema.MappedFeatureType;
063: import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
064: import org.deegree.io.datastore.schema.MappedPropertyType;
065: import org.deegree.io.datastore.schema.MappedSimplePropertyType;
066: import org.deegree.io.datastore.schema.TableRelation;
067: import org.deegree.io.datastore.schema.TableRelation.FK_INFO;
068: import org.deegree.io.datastore.schema.content.MappingField;
069: import org.deegree.io.datastore.schema.content.MappingGeometryField;
070: import org.deegree.io.datastore.schema.content.SimpleContent;
071: import org.deegree.io.datastore.sql.AbstractRequestHandler;
072: import org.deegree.io.datastore.sql.StatementBuffer;
073: import org.deegree.io.datastore.sql.TableAliasGenerator;
074: import org.deegree.io.datastore.sql.transaction.delete.DeleteHandler;
075: import org.deegree.io.datastore.sql.transaction.insert.InsertHandler;
076: import org.deegree.model.feature.Feature;
077: import org.deegree.model.feature.FeatureProperty;
078: import org.deegree.model.feature.schema.FeaturePropertyType;
079: import org.deegree.model.filterencoding.Filter;
080: import org.deegree.model.spatialschema.Geometry;
081: import org.deegree.ogcbase.PropertyPath;
082: import org.deegree.ogcwebservices.wfs.operation.transaction.Insert;
083: import org.deegree.ogcwebservices.wfs.operation.transaction.Transaction;
084: import org.deegree.ogcwebservices.wfs.operation.transaction.Update;
085:
086: /**
087: * Handler for {@link Update} operations (usually contained in {@link Transaction} requests).
088: *
089: * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
090: * @author last edited by: $Author: apoth $
091: *
092: * @version $Revision: 9342 $, $Date: 2007-12-27 04:32:57 -0800 (Thu, 27 Dec 2007) $
093: */
094: public class UpdateHandler extends AbstractRequestHandler {
095:
096: private static final ILogger LOG = LoggerFactory
097: .getLogger(UpdateHandler.class);
098:
099: private SQLTransaction dsTa;
100:
101: private String lockId;
102:
103: /**
104: * Creates a new <code>UpdateHandler</code> from the given parameters.
105: *
106: * @param dsTa
107: * @param aliasGenerator
108: * @param conn
109: * @param lockId
110: * optional id of associated lock (may be null)
111: */
112: public UpdateHandler(SQLTransaction dsTa,
113: TableAliasGenerator aliasGenerator, Connection conn,
114: String lockId) {
115: super (dsTa.getDatastore(), aliasGenerator, conn);
116: this .dsTa = dsTa;
117: this .lockId = lockId;
118: }
119:
120: /**
121: * Performs an update operation against the associated datastore.
122: *
123: * @param ft
124: * @param replacementProps
125: * @param filter
126: * @return number of updated (root) feature instances
127: * @throws DatastoreException
128: */
129: public int performUpdate(MappedFeatureType ft,
130: Map<PropertyPath, FeatureProperty> replacementProps,
131: Filter filter) throws DatastoreException {
132:
133: List<FeatureId> fids = determineAffectedAndModifiableFIDs(ft,
134: filter, this .lockId);
135:
136: LOG.logDebug("Updating: " + ft);
137: for (PropertyPath property : replacementProps.keySet()) {
138: FeatureProperty propertyValue = replacementProps
139: .get(property);
140: for (FeatureId fid : fids) {
141: LOG.logDebug("Updating feature: " + fid);
142: performUpdate(fid, ft, property, propertyValue);
143: }
144: }
145: return fids.size();
146: }
147:
148: /**
149: * Performs an update operation against the associated datastore.
150: * <p>
151: * The filter must match exactly one feature instance (or none) which is then replaced by the
152: * specified replacement feature.
153: *
154: * @param mappedFeatureType
155: * @param replacementFeature
156: * @param filter
157: * @return number of updated (root) feature instances (0 or 1)
158: * @throws DatastoreException
159: */
160: public int performUpdate(MappedFeatureType mappedFeatureType,
161: Feature replacementFeature, Filter filter)
162: throws DatastoreException {
163:
164: LOG.logDebug("Updating (replace): " + mappedFeatureType);
165: if (filter != null) {
166: LOG.logDebug(" filter: " + filter.toXML());
167: }
168:
169: List<FeatureId> fids = determineAffectedAndModifiableFIDs(
170: mappedFeatureType, filter, this .lockId);
171:
172: if (fids.size() > 1) {
173: String msg = Messages
174: .getMessage("DATASTORE_MORE_THAN_ONE_FEATURE");
175: throw new DatastoreException(msg);
176: }
177: DeleteHandler deleteHandler = new DeleteHandler(this .dsTa,
178: this .aliasGenerator, this .conn, this .lockId);
179: deleteHandler.performDelete(mappedFeatureType, filter);
180:
181: // identify stored subfeatures / assign feature ids
182: FeatureIdAssigner fidAssigner = new FeatureIdAssigner(
183: Insert.ID_GEN.GENERATE_NEW);
184: fidAssigner.assignFID(replacementFeature, this .dsTa);
185: // TODO remove this hack
186: fidAssigner.markStoredFeatures();
187:
188: InsertHandler insertHandler = new InsertHandler(this .dsTa,
189: this .aliasGenerator, this .conn);
190: List<Feature> features = new ArrayList<Feature>();
191: features.add(replacementFeature);
192: insertHandler.performInsert(features);
193:
194: return fids.size();
195: }
196:
197: /**
198: * Performs the update (replacing of a property) of the given feature instance.
199: * <p>
200: * If the selected property is a direct property of the feature, the root feature is updated,
201: * otherwise the targeted subfeatures have to be determined first.
202: *
203: * @param fid
204: * @param ft
205: * @param propertyName
206: * @param replacementProperty
207: * @throws DatastoreException
208: */
209: private void performUpdate(FeatureId fid, MappedFeatureType ft,
210: PropertyPath propertyName,
211: FeatureProperty replacementProperty)
212: throws DatastoreException {
213:
214: Object replacementValue = replacementProperty.getValue();
215: LOG.logDebug("Updating fid: " + fid + ", propertyName: "
216: + propertyName + " -> " + replacementValue);
217:
218: int steps = propertyName.getSteps();
219: QualifiedName propName = propertyName.getStep(steps - 1)
220: .getPropertyName();
221: if (steps > 2) {
222: QualifiedName subFtName = propertyName.getStep(steps - 2)
223: .getPropertyName();
224: MappedFeatureType subFt = this .datastore
225: .getFeatureType(subFtName);
226: MappedPropertyType pt = (MappedPropertyType) subFt
227: .getProperty(propName);
228: List<TableRelation> tablePath = getTablePath(ft,
229: propertyName);
230: List<FeatureId> subFids = determineAffectedFIDs(fid, subFt,
231: tablePath);
232: for (FeatureId subFid : subFids) {
233: updateProperty(subFid, subFt, pt, replacementValue);
234: }
235: } else {
236: MappedPropertyType pt = (MappedPropertyType) ft
237: .getProperty(propName);
238: updateProperty(fid, ft, pt, replacementValue);
239: }
240: }
241:
242: /**
243: * Determines the subfeature instances that are targeted by the given PropertyName.
244: *
245: * @param fid
246: * @param subFt
247: * @param propertyName
248: * @return the matched feature ids
249: * @throws DatastoreException
250: */
251: private List<FeatureId> determineAffectedFIDs(FeatureId fid,
252: MappedFeatureType subFt, List<TableRelation> path)
253: throws DatastoreException {
254:
255: List<FeatureId> subFids = new ArrayList<FeatureId>();
256:
257: this .aliasGenerator.reset();
258: String[] tableAliases = this .aliasGenerator
259: .generateUniqueAliases(path.size() + 1);
260: String toTableAlias = tableAliases[tableAliases.length - 1];
261: StatementBuffer query = new StatementBuffer();
262: query.append("SELECT ");
263: appendFeatureIdColumns(subFt, toTableAlias, query);
264: query.append(" FROM ");
265: query.append(path.get(0).getFromTable());
266: query.append(" ");
267: query.append(tableAliases[0]);
268: // append joins
269: for (int i = 0; i < path.size(); i++) {
270: query.append(" JOIN ");
271: query.append(path.get(i).getToTable());
272: query.append(" ");
273: query.append(tableAliases[i + 1]);
274: query.append(" ON ");
275: MappingField[] fromFields = path.get(i).getFromFields();
276: MappingField[] toFields = path.get(i).getToFields();
277: for (int j = 0; j < fromFields.length; j++) {
278: query.append(tableAliases[i]);
279: query.append('.');
280: query.append(fromFields[j].getField());
281: query.append('=');
282: query.append(tableAliases[i + 1]);
283: query.append('.');
284: query.append(toFields[j].getField());
285: }
286: }
287: query.append(" WHERE ");
288: MappingField[] fidFields = fid.getFidDefinition().getIdFields();
289: for (int i = 0; i < fidFields.length; i++) {
290: query.append(tableAliases[0]);
291: query.append('.');
292: query.append(fidFields[i].getField());
293: query.append("=?");
294: query.addArgument(fid.getValue(i), fidFields[i].getType());
295: if (i != fidFields.length - 1) {
296: query.append(" AND ");
297: }
298: }
299:
300: PreparedStatement stmt = null;
301: ResultSet rs = null;
302: try {
303: stmt = this .datastore.prepareStatement(conn, query);
304: rs = stmt.executeQuery();
305: subFids = extractFeatureIds(rs, subFt);
306: } catch (SQLException e) {
307: throw new DatastoreException(
308: "Error in determineAffectedFIDs(): "
309: + e.getMessage());
310: } finally {
311: try {
312: if (rs != null) {
313: try {
314: rs.close();
315: } catch (SQLException e) {
316: LOG.logError("Error closing result set: '"
317: + e.getMessage() + "'.", e);
318: }
319: }
320: } finally {
321: if (stmt != null) {
322: try {
323: stmt.close();
324: } catch (SQLException e) {
325: LOG.logError("Error closing statement: '"
326: + e.getMessage() + "'.", e);
327: }
328: }
329: }
330: }
331: return subFids;
332: }
333:
334: /**
335: * Returns the relations (the "path") that lead from the feature type's table to the subfeature
336: * table which is targeted by the specified property name.
337: *
338: * @param ft
339: * source feature type
340: * @param path
341: * property name
342: * @return relations that lead from the feature type's table to the subfeature table
343: */
344: private List<TableRelation> getTablePath(MappedFeatureType ft,
345: PropertyPath path) {
346: List<TableRelation> relations = new ArrayList<TableRelation>();
347: for (int i = 1; i < path.getSteps() - 2; i += 2) {
348: QualifiedName propName = path.getStep(i).getPropertyName();
349: MappedFeaturePropertyType pt = (MappedFeaturePropertyType) ft
350: .getProperty(propName);
351: TableRelation[] tableRelations = pt.getTableRelations();
352: for (int j = 0; j < tableRelations.length; j++) {
353: relations.add(tableRelations[j]);
354: }
355: ft = pt.getFeatureTypeReference().getFeatureType();
356: }
357: return relations;
358: }
359:
360: /**
361: * Replaces the specified feature's property with the given value.
362: *
363: * @param fid
364: * @param ft
365: * @param pt
366: * @param replacementValue
367: * @throws DatastoreException
368: */
369: private void updateProperty(FeatureId fid, MappedFeatureType ft,
370: MappedPropertyType pt, Object replacementValue)
371: throws DatastoreException {
372: LOG.logDebug("Updating property '" + pt.getName()
373: + "' of feature '" + fid + "'.");
374:
375: if (!ft.isUpdatable()) {
376: String msg = Messages.getMessage(
377: "DATASTORE_FT_NOT_UPDATABLE", ft.getName());
378: throw new DatastoreException(msg);
379: }
380: TableRelation[] tablePath = pt.getTableRelations();
381: if (pt instanceof MappedSimplePropertyType) {
382: SimpleContent content = ((MappedSimplePropertyType) pt)
383: .getContent();
384: if (content.isUpdateable()) {
385: if (content instanceof MappingField) {
386: updateProperty(fid, tablePath,
387: (MappingField) content, replacementValue);
388: }
389: } else {
390: LOG.logInfo("Ignoring property '" + pt.getName()
391: + "' in update - is virtual.");
392: }
393: } else if (pt instanceof MappedGeometryPropertyType) {
394: MappingGeometryField dbField = ((MappedGeometryPropertyType) pt)
395: .getMappingField();
396: Object dbGeometry = this .datastore
397: .convertDeegreeToDBGeometry(
398: (Geometry) replacementValue, dbField
399: .getSRS(), this .conn);
400: // TODO remove this Oracle hack
401: if (this .datastore.getClass().getName().contains(
402: "OracleDatastore")) {
403: dbField = new MappingGeometryField(dbField.getTable(),
404: dbField.getField(), Types.STRUCT, dbField
405: .getSRS());
406: }
407: updateProperty(fid, tablePath, dbField, dbGeometry);
408: } else if (pt instanceof FeaturePropertyType) {
409: updateProperty(fid, ft, (MappedFeaturePropertyType) pt,
410: (Feature) replacementValue);
411: } else {
412: throw new DatastoreException(
413: "Internal error: Properties with type '"
414: + pt.getClass()
415: + "' are not handled in UpdateHandler.");
416: }
417: }
418:
419: /**
420: * Updates a simple / geometry property of the specified feature.
421: * <p>
422: * Three cases are distinguished (which all have to be handled differently):
423: * <ol>
424: * <li>property value stored in feature table</li>
425: * <li>property value stored in property table, fk in property table</li>
426: * <li>property value stored in property table, fk in feature table</li>
427: * </ol>
428: *
429: * @param fid
430: * @param tablePath
431: * @param dbField
432: * @param replacementValue
433: * @throws DatastoreException
434: */
435: private void updateProperty(FeatureId fid,
436: TableRelation[] tablePath, MappingField dbField,
437: Object replacementValue) throws DatastoreException {
438:
439: if (tablePath.length == 0) {
440: updateProperty(fid, dbField, replacementValue);
441: } else if (tablePath.length == 1) {
442: TableRelation relation = tablePath[0];
443: if (tablePath[0].getFKInfo() == FK_INFO.fkIsToField) {
444: Object[] keyValues = determineKeyValues(fid, relation);
445: if (keyValues != null) {
446: deletePropertyRows(relation, keyValues);
447: }
448: if (replacementValue != null) {
449: insertPropertyRow(relation, keyValues, dbField,
450: replacementValue);
451: }
452: } else {
453: Object[] oldKeyValues = determineKeyValues(fid,
454: relation);
455: Object[] newKeyValues = findOrInsertPropertyRow(
456: relation, dbField, replacementValue);
457: updateFeatureRow(fid, relation, newKeyValues);
458: if (oldKeyValues != null) {
459: deleteOrphanedPropertyRows(relation, oldKeyValues);
460: }
461: }
462: } else {
463: throw new DatastoreException(
464: "Updating of properties that are stored in "
465: + "related tables using join tables is not "
466: + "supported.");
467: }
468: }
469:
470: private void updateFeatureRow(FeatureId fid,
471: TableRelation relation, Object[] newKeyValues)
472: throws DatastoreException {
473:
474: StatementBuffer query = new StatementBuffer();
475: query.append("UPDATE ");
476: query.append(relation.getFromTable());
477: query.append(" SET ");
478: MappingField[] fromFields = relation.getFromFields();
479: for (int i = 0; i < newKeyValues.length; i++) {
480: query.append(fromFields[i].getField());
481: query.append("=?");
482: query.addArgument(newKeyValues[i], fromFields[i].getType());
483: }
484: query.append(" WHERE ");
485: appendFIDWhereCondition(query, fid);
486:
487: LOG.logDebug("Performing update: " + query.getQueryString());
488:
489: PreparedStatement stmt = null;
490: try {
491: stmt = this .datastore.prepareStatement(conn, query);
492: stmt.execute();
493: } catch (SQLException e) {
494: throw new DatastoreException("Error in performUpdate(): "
495: + e.getMessage());
496: } finally {
497: if (stmt != null) {
498: try {
499: stmt.close();
500: } catch (SQLException e) {
501: LOG.logError("Error closing statement: '"
502: + e.getMessage() + "'.", e);
503: }
504: }
505: }
506:
507: }
508:
509: /**
510: * Updates a simple / geometry property of the specified feature.
511: * <p>
512: * This method handles the case where the property is stored in the feature table itself, so a
513: * single UPDATE statement is sufficient.
514: *
515: * @param fid
516: * @param dbField
517: * @param replacementValue
518: * @throws DatastoreException
519: */
520: private void updateProperty(FeatureId fid, MappingField dbField,
521: Object replacementValue) throws DatastoreException {
522:
523: StatementBuffer query = new StatementBuffer();
524: query.append("UPDATE ");
525: query.append(dbField.getTable());
526: query.append(" SET ");
527: query.append(dbField.getField());
528: query.append("=?");
529: query.addArgument(replacementValue, dbField.getType());
530: query.append(" WHERE ");
531: appendFIDWhereCondition(query, fid);
532:
533: LOG.logDebug("Performing update: " + query.getQueryString());
534:
535: PreparedStatement stmt = null;
536: try {
537: stmt = this .datastore.prepareStatement(conn, query);
538: stmt.execute();
539: } catch (SQLException e) {
540: throw new DatastoreException("Error in performUpdate(): "
541: + e.getMessage());
542: } finally {
543: if (stmt != null) {
544: try {
545: stmt.close();
546: } catch (SQLException e) {
547: LOG.logError("Error closing statement: '"
548: + e.getMessage() + "'.", e);
549: }
550: }
551: }
552: }
553:
554: /**
555: * Determines the values for the key columns that are referenced by the given table relation (as
556: * from fields).
557: *
558: * @param fid
559: * @param relation
560: * @return the values for the key columns
561: * @throws DatastoreException
562: */
563: private Object[] determineKeyValues(FeatureId fid,
564: TableRelation relation) throws DatastoreException {
565:
566: StatementBuffer query = new StatementBuffer();
567: query.append("SELECT ");
568: MappingField[] fromFields = relation.getFromFields();
569: for (int i = 0; i < fromFields.length; i++) {
570: query.append(fromFields[i].getField());
571: if (i != fromFields.length - 1) {
572: query.append(',');
573: }
574: }
575: query.append(" FROM ");
576: query.append(relation.getFromTable());
577: query.append(" WHERE ");
578: appendFIDWhereCondition(query, fid);
579:
580: Object[] keyValues = new Object[fromFields.length];
581: LOG.logDebug("determineKeyValues: " + query.getQueryString());
582: PreparedStatement stmt = null;
583: try {
584: stmt = this .datastore.prepareStatement(conn, query);
585: ResultSet rs = stmt.executeQuery();
586: if (rs.next()) {
587: for (int i = 0; i < keyValues.length; i++) {
588: Object value = rs.getObject(i + 1);
589: if (value != null) {
590: keyValues[i] = value;
591: } else {
592: keyValues = null;
593: break;
594: }
595: }
596: } else {
597: LOG
598: .logError("Internal error. Result is empty (no rows).");
599: throw new SQLException();
600: }
601: } catch (SQLException e) {
602: throw new DatastoreException("Error in performUpdate(): "
603: + e.getMessage());
604: } finally {
605: if (stmt != null) {
606: try {
607: stmt.close();
608: } catch (SQLException e) {
609: LOG.logError("Error closing statement: '"
610: + e.getMessage() + "'.", e);
611: }
612: }
613: }
614: return keyValues;
615: }
616:
617: private void deletePropertyRows(TableRelation relation,
618: Object[] keyValues) throws DatastoreException {
619:
620: StatementBuffer query = new StatementBuffer();
621: query.append("DELETE FROM ");
622: query.append(relation.getToTable());
623: query.append(" WHERE ");
624: MappingField[] toFields = relation.getToFields();
625: for (int i = 0; i < toFields.length; i++) {
626: query.append(toFields[i].getField());
627: query.append("=?");
628: query.addArgument(keyValues[i], toFields[i].getType());
629: if (i != toFields.length - 1) {
630: query.append(" AND ");
631: }
632: }
633:
634: PreparedStatement stmt = null;
635: LOG.logDebug("deletePropertyRows: " + query.getQueryString());
636: try {
637: stmt = this .datastore.prepareStatement(conn, query);
638: stmt.execute();
639: } catch (SQLException e) {
640: throw new DatastoreException("Error in performUpdate(): "
641: + e.getMessage());
642: } finally {
643: if (stmt != null) {
644: try {
645: stmt.close();
646: } catch (SQLException e) {
647: LOG.logError("Error closing statement: '"
648: + e.getMessage() + "'.", e);
649: }
650: }
651: }
652: }
653:
654: private void insertPropertyRow(TableRelation relation,
655: Object[] keyValues, MappingField dbField,
656: Object replacementValue) throws DatastoreException {
657:
658: if (keyValues == null) {
659: if (relation.getFromFields().length > 1) {
660: throw new DatastoreException(
661: "Key generation for compound keys is not supported.");
662: }
663: // generate new primary key
664: keyValues = new Object[1];
665: keyValues[0] = relation.getIdGenerator().getNewId(dsTa);
666: }
667:
668: StatementBuffer query = new StatementBuffer();
669: query.append("INSERT INTO ");
670: query.append(relation.getToTable());
671: query.append(" (");
672: MappingField[] toFields = relation.getToFields();
673: for (int i = 0; i < toFields.length; i++) {
674: query.append(toFields[i].getField());
675: query.append(',');
676: }
677: query.append(dbField.getField());
678: query.append(") VALUES (");
679: for (int i = 0; i < toFields.length; i++) {
680: query.append('?');
681: query.addArgument(keyValues[i], toFields[i].getType());
682: query.append(',');
683: }
684: query.append("?)");
685: query.addArgument(replacementValue, dbField.getType());
686:
687: PreparedStatement stmt = null;
688: LOG.logDebug("insertPropertyRow: " + query.getQueryString());
689: try {
690: stmt = this .datastore.prepareStatement(conn, query);
691: stmt.execute();
692: } catch (SQLException e) {
693: throw new DatastoreException("Error in performUpdate(): "
694: + e.getMessage());
695: } finally {
696: if (stmt != null) {
697: try {
698: stmt.close();
699: } catch (SQLException e) {
700: LOG.logError("Error closing statement: '"
701: + e.getMessage() + "'.", e);
702: }
703: }
704: }
705: }
706:
707: /**
708: * Returns the foreign key value(s) for the row that stores the given property.
709: * <p>
710: * If the row already exists, the existing key is returned, otherwise a new row for the property
711: * is inserted first.
712: *
713: * @param relation
714: * @param dbField
715: * @param replacementValue
716: * @return foreign key value(s) for the row that stores the given property
717: * @throws DatastoreException
718: */
719: private Object[] findOrInsertPropertyRow(TableRelation relation,
720: MappingField dbField, Object replacementValue)
721: throws DatastoreException {
722:
723: Object[] keyValues = null;
724:
725: if (dbField.getType() != Types.GEOMETRY) {
726: StatementBuffer query = new StatementBuffer();
727: query.append("SELECT ");
728: MappingField[] toFields = relation.getToFields();
729: for (int i = 0; i < toFields.length; i++) {
730: query.append(toFields[i].getField());
731: if (i != toFields.length - 1) {
732: query.append(',');
733: }
734: }
735: query.append(" FROM ");
736: query.append(relation.getToTable());
737: query.append(" WHERE ");
738: query.append(dbField.getField());
739: query.append("=?");
740: query.addArgument(replacementValue, dbField.getType());
741:
742: PreparedStatement stmt = null;
743: LOG.logDebug("findOrInsertPropertyRow: "
744: + query.getQueryString());
745: try {
746: stmt = this .datastore.prepareStatement(conn, query);
747: ResultSet rs = stmt.executeQuery();
748: if (rs.next()) {
749: keyValues = new Object[toFields.length];
750: for (int i = 0; i < toFields.length; i++) {
751: keyValues[i] = rs.getObject(i + 1);
752: }
753: }
754: } catch (SQLException e) {
755: throw new DatastoreException(
756: "Error in findOrInsertPropertyRow(): "
757: + e.getMessage());
758: } finally {
759: if (stmt != null) {
760: try {
761: stmt.close();
762: } catch (SQLException e) {
763: LOG.logError("Error closing statement: '"
764: + e.getMessage() + "'.", e);
765: }
766: }
767: }
768: if (keyValues != null) {
769: return keyValues;
770: }
771: }
772:
773: if (relation.getToFields().length > 1) {
774: throw new DatastoreException(
775: "Key generation for compound keys is not supported.");
776: }
777:
778: // property does not yet exist (or it's a geometry)
779: keyValues = new Object[1];
780: // generate new PK
781: keyValues[0] = relation.getNewPK(this .dsTa);
782: insertPropertyRow(relation, keyValues, dbField,
783: replacementValue);
784:
785: return keyValues;
786: }
787:
788: private void deleteOrphanedPropertyRows(TableRelation relation,
789: Object[] keyValues) throws DatastoreException {
790: DeleteHandler deleteHandler = new DeleteHandler(this .dsTa,
791: this .aliasGenerator, this .conn, this .lockId);
792: deleteHandler.deleteOrphanedPropertyRows(relation, keyValues);
793: }
794:
795: private void updateProperty(@SuppressWarnings("unused")
796: FeatureId fid, @SuppressWarnings("unused")
797: MappedFeatureType ft, @SuppressWarnings("unused")
798: MappedFeaturePropertyType pt, @SuppressWarnings("unused")
799: Feature replacementFeature) {
800: throw new UnsupportedOperationException(
801: "Updating of feature properties is not implemented yet.");
802: }
803:
804: private void appendFIDWhereCondition(StatementBuffer query,
805: FeatureId fid) {
806: MappingField[] fidFields = fid.getFidDefinition().getIdFields();
807: for (int i = 0; i < fidFields.length; i++) {
808: query.append(fidFields[i].getField());
809: query.append("=?");
810: query.addArgument(fid.getValue(i), fidFields[i].getType());
811: if (i != fidFields.length - 1) {
812: query.append(" AND ");
813: }
814: }
815: }
816: }
|