private int deleteFeatureRow(IdAnalysis analysis) throws FeatureStoreException {
    int deleted = 0;
    FeatureTypeMapping ftMapping = schema.getFtMapping(analysis.getFeatureType().getName());
    FIDMapping fidMapping = ftMapping.getFidMapping();
    PreparedStatement stmt = null;
    try {
      StringBuilder sql = new StringBuilder("DELETE FROM " + ftMapping.getFtTable() + " WHERE ");
      sql.append(fidMapping.getColumns().get(0).first);
      sql.append("=?");
      for (int i = 1; i < fidMapping.getColumns().size(); i++) {
        sql.append(" AND ");
        sql.append(fidMapping.getColumns().get(i));
        sql.append("=?");
      }
      stmt = conn.prepareStatement(sql.toString());

      int i = 1;
      for (String fidKernel : analysis.getIdKernels()) {
        PrimitiveType pt = new PrimitiveType(fidMapping.getColumns().get(i - 1).second);
        PrimitiveValue value = new PrimitiveValue(fidKernel, pt);
        Object sqlValue = SQLValueMangler.internalToSQL(value);
        stmt.setObject(i++, sqlValue);
      }
      LOG.debug("Executing: " + stmt);
      deleted += stmt.executeUpdate();
    } catch (Throwable e) {
      LOG.error(e.getMessage(), e);
      throw new FeatureStoreException(e.getMessage(), e);
    } finally {
      JDBCUtils.close(stmt);
    }
    return deleted;
  }
  private void setRelationalUpdateValues(
      List<ParsedPropertyReplacement> replacementProps,
      FeatureTypeMapping ftMapping,
      PreparedStatement stmt,
      IdFilter filter,
      FIDMapping fidMapping)
      throws SQLException {
    int i = 1;

    for (ParsedPropertyReplacement replacement : replacementProps) {
      Property replacementProp = replacement.getNewValue();
      QName propName = replacementProp.getType().getName();
      Mapping mapping = ftMapping.getMapping(propName);
      if (mapping != null) {
        if (mapping.getJoinedTable() != null && !mapping.getJoinedTable().isEmpty()) {
          continue;
        }

        Object value = replacementProp.getValue();
        if (value != null) {
          ParticleConverter<TypedObjectNode> converter =
              (ParticleConverter<TypedObjectNode>) fs.getConverter(mapping);
          if (mapping instanceof PrimitiveMapping) {
            MappingExpression me = ((PrimitiveMapping) mapping).getMapping();
            if (!(me instanceof DBField)) {
              continue;
            }
            converter.setParticle(stmt, (PrimitiveValue) value, i++);
          } else if (mapping instanceof GeometryMapping) {
            MappingExpression me = ((GeometryMapping) mapping).getMapping();
            if (!(me instanceof DBField)) {
              continue;
            }
            converter.setParticle(stmt, (Geometry) value, i++);
          }
        } else {
          stmt.setObject(i++, null);
        }
      }
    }

    for (String id : filter.getMatchingIds()) {
      IdAnalysis analysis = schema.analyzeId(id);
      int j = i;
      for (String fidKernel : analysis.getIdKernels()) {
        PrimitiveValue value =
            new PrimitiveValue(fidKernel, new PrimitiveType(fidMapping.getColumnType()));
        Object sqlValue = SQLValueMangler.internalToSQL(value);
        stmt.setObject(j++, sqlValue);
      }
      stmt.addBatch();
    }
  }
  /**
   * Deletes the joined rows for the specified feature id.
   *
   * <p>Deletes all joined rows and transitive join rows, but stops at joins to subfeature tables.
   *
   * @param fid feature id, must not be <code>null</code>
   * @throws FeatureStoreException
   */
  private void deleteJoinedRows(IdAnalysis fid) throws FeatureStoreException {

    Map<SQLIdentifier, Object> keyColsToValues = new HashMap<SQLIdentifier, Object>();

    FeatureTypeMapping ftMapping = schema.getFtMapping(fid.getFeatureType().getName());

    // add values for feature id columns
    int i = 0;
    for (Pair<SQLIdentifier, BaseType> fidColumns : ftMapping.getFidMapping().getColumns()) {
      PrimitiveType pt = new PrimitiveType(fidColumns.second);
      PrimitiveValue value = new PrimitiveValue(fid.getIdKernels()[i], pt);
      Object sqlValue = SQLValueMangler.internalToSQL(value);
      keyColsToValues.put(fidColumns.first, sqlValue);
      i++;
    }

    // traverse mapping particles
    for (Mapping particle : ftMapping.getMappings()) {
      deleteJoinedRows(particle, keyColsToValues);
    }
  }