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();
    }
  }
  private String createRelationalUpdateStatement(
      FeatureTypeMapping ftMapping,
      FIDMapping fidMapping,
      List<ParsedPropertyReplacement> replacementProps,
      List<ResourceId> list)
      throws FilterEvaluationException, FeatureStoreException, SQLException {
    StringBuffer sql = new StringBuffer("UPDATE ");
    sql.append(ftMapping.getFtTable());
    sql.append(" SET ");
    boolean first = true;
    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()) {
          addRelationallyMappedMultiProperty(replacement, mapping, ftMapping, list);
          continue;
        }
        String column = null;
        ParticleConverter<TypedObjectNode> converter =
            (ParticleConverter<TypedObjectNode>) fs.getConverter(mapping);
        if (mapping instanceof PrimitiveMapping) {
          MappingExpression me = ((PrimitiveMapping) mapping).getMapping();
          if (!(me instanceof DBField)) {
            continue;
          }
          column = ((DBField) me).getColumn();
          if (!first) {
            sql.append(",");
          } else {
            first = false;
          }
          sql.append(column);
          sql.append("=");

          // TODO communicate value for non-prepared statement converters
          sql.append(converter.getSetSnippet(null));
        } else if (mapping instanceof GeometryMapping) {
          MappingExpression me = ((GeometryMapping) mapping).getMapping();
          if (!(me instanceof DBField)) {
            continue;
          }
          column = ((DBField) me).getColumn();
          if (!first) {
            sql.append(",");
          } else {
            first = false;
          }
          sql.append(column);
          sql.append("=");
          // TODO communicate value for non-prepared statement converters
          sql.append(converter.getSetSnippet(null));
        } else {
          LOG.warn(
              "Updating of " + mapping.getClass() + " is currently not implemented. Omitting.");
          continue;
        }
      } else {
        LOG.warn("No mapping for update property '" + propName + "'. Omitting.");
      }
    }

    // only property changes in multi properties?
    if (first) {
      return null;
    }

    sql.append(" 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("=?");
    }
    return sql.toString();
  }
  private void deleteJoinedRows(Mapping particle, Map<SQLIdentifier, Object> keyColToValue)
      throws FeatureStoreException {

    // TODO: After FeatureTypeJoin is introduced, rework this case (may allow joins)
    if (particle instanceof FeatureMapping) {
      return;
    }

    // determine and delete joined rows
    if (particle.getJoinedTable() != null && !particle.getJoinedTable().isEmpty()) {
      TableJoin tableJoin = particle.getJoinedTable().get(0);

      PreparedStatement stmt = null;
      try {
        StringBuilder sql = new StringBuilder("SELECT ");
        boolean first = true;
        for (SQLIdentifier selectColumn : tableJoin.getToColumns()) {
          if (!first) {
            sql.append(',');
          } else {
            first = false;
          }
          sql.append("X2.");
          sql.append(selectColumn);
        }
        sql.append(" FROM ");
        sql.append(tableJoin.getFromTable());
        sql.append(" X1,");
        sql.append(tableJoin.getToTable());
        sql.append(" X2");
        sql.append(" WHERE");

        first = true;
        int i = 0;
        for (SQLIdentifier fromColumn : tableJoin.getFromColumns()) {
          SQLIdentifier toColumn = tableJoin.getToColumns().get(i++);
          if (!first) {
            sql.append(',');
          } else {
            first = false;
          }
          sql.append(" X1.");
          sql.append(fromColumn);
          sql.append("=");
          sql.append("X2.");
          sql.append(toColumn);
          first = false;
        }

        for (Entry<SQLIdentifier, Object> joinKey : keyColToValue.entrySet()) {
          sql.append(" AND X1.");
          sql.append(joinKey.getKey());
          sql.append("=?");
          first = false;
        }

        stmt = conn.prepareStatement(sql.toString());

        i = 1;
        for (Entry<SQLIdentifier, Object> joinKey : keyColToValue.entrySet()) {
          stmt.setObject(i++, joinKey.getValue());
        }
        LOG.debug("Executing SELECT (following join): " + stmt);
        ResultSet rs = stmt.executeQuery();
        while (rs.next()) {
          Map<SQLIdentifier, Object> joinKeyToValue = new HashMap<SQLIdentifier, Object>();
          i = 1;
          for (SQLIdentifier toColumn : tableJoin.getToColumns()) {
            joinKeyToValue.put(toColumn, rs.getObject(i++));
          }
          deleteJoinedRows(particle, tableJoin, joinKeyToValue);
        }
      } catch (SQLException e) {
        LOG.error(e.getMessage(), e);
        throw new FeatureStoreException(e.getMessage(), e);
      } finally {
        JDBCUtils.close(stmt);
      }
    } else {
      // process compound particle structure
      if (particle instanceof CompoundMapping) {
        CompoundMapping cm = (CompoundMapping) particle;
        for (Mapping child : cm.getParticles()) {
          deleteJoinedRows(child, keyColToValue);
        }
      }
    }
  }