This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch CAMEL-26067-jdbc-plain-placeholders
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 6d32c3c8b01a3afad0b2001ebf3ff6bfe8fa0106
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Nov 5 21:29:14 2026 +0100

    CAMEL-26067: camel-jdbc - plain ? placeholders take their values only from 
the CamelJdbcParameters header
    
    With useHeadersAsParameters=true and a query with only plain ? placeholders,
    the jdbc and spring-jdbc producers bound the values of all the message 
headers
    by position (or of the CamelJdbcParameters map when set). Which value went 
to
    which ? depended on the headers of the message and their order.
    
    The values of plain ? placeholders now come only from the 
CamelJdbcParameters
    header: a List in the order of the placeholders, or a Map whose values are 
taken
    in the order of its entries. When the header is not set, or is of another 
type,
    the exchange fails with a clear message. Named parameters work as before.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/catalog/components/jdbc.json  |  6 +-
 .../camel/catalog/components/spring-jdbc.json      |  6 +-
 .../apache/camel/catalog/docs/jdbc-component.adoc  | 16 ++++++
 .../org/apache/camel/component/jdbc/jdbc.json      |  6 +-
 .../camel-jdbc/src/main/docs/jdbc-component.adoc   | 16 ++++++
 .../jdbc/DefaultJdbcPrepareStatementStrategy.java  | 50 ++++++++++++-----
 .../apache/camel/component/jdbc/JdbcConstants.java |  8 ++-
 .../apache/camel/component/jdbc/JdbcEndpoint.java  |  9 ++-
 .../component/jdbc/JdbcParameterizedQueryTest.java | 65 ++++++++++++++++++++--
 .../camel/component/spring/jdbc/spring-jdbc.json   |  6 +-
 .../component/spring/jdbc/SpringJdbcEndpoint.java  |  5 +-
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    | 13 +++++
 .../endpoint/dsl/JdbcEndpointBuilderFactory.java   | 16 ++++--
 .../dsl/SpringJdbcEndpointBuilderFactory.java      | 16 ++++--
 14 files changed, 189 insertions(+), 49 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json
index e05623176303..1693491f416d 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json
@@ -33,14 +33,14 @@
     "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is an UPDATE, query the update count is 
returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" },
     "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is a SELECT and the outputType is 
SelectList, the row count is returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" },
     "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Set<String>", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The column names from the ResultSet as a 
java.util.Set type.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" },
-    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "Map", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "A java.util.Map which has the headers to be used if 
useHeadersAsParameters has been enabled.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" },
+    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Object", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The values of the query parameters when 
useHeadersAsParameters has been enabled: a java.util.Map with the values of the 
named parameters, or a java.util.List with the values of the positional 
placeholders, in order.", "constantName":  [...]
     "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "false", "description": "Set its value 
to true to retrieve generated keys", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" },
     "CamelGeneratedColumns": { "index": 5, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"String[] or int[]", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Set it to specify the expected 
generated columns", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_COLUMNS" },
     "CamelGeneratedKeysRowCount": { "index": 6, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The number of rows in the header that 
contains generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_ROW_COUNT" },
     "CamelGeneratedKeysRows": { "index": 7, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"List<Map<String, Object>>", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Rows that contains the 
generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_DATA" }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its placeholders come from the 
CamelJdbcParameters header, or else from the headers" },
+    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its named parameters (:?name) come from 
the CamelJdbcParameters header, or else from the headers, and the values of 
plain ? placeholders, in order, only from the CamelJdbcParameters header" },
     "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "outputType", "value": "SelectOne", "carriers": [ "map", "text", 
"value", "bytes", "pojo", "null", "unchanged" ], "javaTypeOption": 
"outputClass" }, { "option": "outputType", "value": "StreamList", "carriers": [ 
"iterator", "unchanged" ] } ], "description": "The rows of a select: a List of 
Maps of the column names and their values (beans of outputClass when set). 
SelectOne gives the Map of the one row, th [...]
   },
   "properties": {
@@ -53,7 +53,7 @@
     "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": 
"Reset Auto Commit", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "description": "Camel will set 
the autoCommit on the JDBC connection to be false, commit the change after 
executed the statement and reset the autoCommit flag of the connection at the 
end, if the resetAutoCommit is true. If [...]
     "transacted": { "index": 7, "kind": "parameter", "displayName": 
"Transacted", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not 
used: the producer does not read this option", "autowired": false, "secret": 
false, "defaultValue": false, "description": "Whether transactions are in use. 
Deprecation note: Not used: the producer does not read this option" },
     "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": 
"Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "To read BLOB 
columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read 
after the producer has closed the connection. This may be needed for certain 
databases such as Oracle where yo [...]
-    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
+    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
     "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", 
"displayName": "Use JDBC4 Column Name And Label Semantics", "group": 
"producer", "label": "", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or 
older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get 
the column name where as JDBC 3.0 uses both [...]
     "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
     "beanRowMapper": { "index": 12, "kind": "parameter", "displayName": "Bean 
Row Mapper", "group": "advanced", "label": "advanced", "required": false, 
"type": "object", "javaType": "org.apache.camel.component.jdbc.BeanRowMapper", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
use a custom org.apache.camel.component.jdbc.BeanRowMapper when using 
outputClass. The default implementation will lower case the row names and skip 
underscores, and dashes. For exampl [...]
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json
index 905518eb3d89..e1c237003a65 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json
@@ -33,14 +33,14 @@
     "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is an UPDATE, query the update count is 
returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" },
     "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is a SELECT and the outputType is 
SelectList, the row count is returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" },
     "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Set<String>", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The column names from the ResultSet as a 
java.util.Set type.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" },
-    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "Map", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "A java.util.Map which has the headers to be used if 
useHeadersAsParameters has been enabled.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" },
+    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Object", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The values of the query parameters when 
useHeadersAsParameters has been enabled: a java.util.Map with the values of the 
named parameters, or a java.util.List with the values of the positional 
placeholders, in order.", "constantName":  [...]
     "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "false", "description": "Set its value 
to true to retrieve generated keys", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" },
     "CamelGeneratedColumns": { "index": 5, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"String[] or int[]", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Set it to specify the expected 
generated columns", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_COLUMNS" },
     "CamelGeneratedKeysRowCount": { "index": 6, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The number of rows in the header that 
contains generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_ROW_COUNT" },
     "CamelGeneratedKeysRows": { "index": 7, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"List<Map<String, Object>>", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Rows that contains the 
generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_DATA" }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its placeholders come from the 
CamelJdbcParameters header, or else from the headers" },
+    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its named parameters (:?name) come from 
the CamelJdbcParameters header, or else from the headers, and the values of 
plain ? placeholders, in order, only from the CamelJdbcParameters header" },
     "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "outputType", "value": "SelectOne", "carriers": [ "map", "text", 
"value", "bytes", "pojo", "null", "unchanged" ], "javaTypeOption": 
"outputClass" }, { "option": "outputType", "value": "StreamList", "carriers": [ 
"iterator", "unchanged" ] } ], "description": "The rows of a select: a List of 
Maps of the column names and their values (beans of outputClass when set). 
SelectOne gives the Map of the one row, th [...]
   },
   "properties": {
@@ -53,7 +53,7 @@
     "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": 
"Reset Auto Commit", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "description": "Camel will set 
the autoCommit on the JDBC connection to be false, commit the change after 
executed the statement and reset the autoCommit flag of the connection at the 
end, if the resetAutoCommit is true. If [...]
     "transacted": { "index": 7, "kind": "parameter", "displayName": 
"Transacted", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not 
used: the producer does not read this option", "autowired": false, "secret": 
false, "defaultValue": false, "description": "Deprecation note: Not used: the 
producer does not read this option" },
     "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": 
"Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "To read BLOB 
columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read 
after the producer has closed the connection. This may be needed for certain 
databases such as Oracle where yo [...]
-    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
+    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
     "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", 
"displayName": "Use JDBC4 Column Name And Label Semantics", "group": 
"producer", "label": "", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or 
older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get 
the column name where as JDBC 3.0 uses both [...]
     "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
     "beanRowMapper": { "index": 12, "kind": "parameter", "displayName": "Bean 
Row Mapper", "group": "advanced", "label": "advanced", "required": false, 
"type": "object", "javaType": "org.apache.camel.component.jdbc.BeanRowMapper", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
use a custom org.apache.camel.component.jdbc.BeanRowMapper when using 
outputClass. The default implementation will lower case the row names and skip 
underscores, and dashes. For exampl [...]
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
index c055e8f3c8b2..5c691ac96fd9 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
@@ -159,6 +159,22 @@ YAML::
 You can also store the header values in a `java.util.Map` and store the
 map on the headers with the key `CamelJdbcParameters`.
 
+=== Using plain ? placeholders
+
+A query with plain `?` placeholders (and no named parameters) takes the values 
of the placeholders, in order, only from
+the `CamelJdbcParameters` header, as a `java.util.List` (a `java.util.Map` 
works too, with its values taken in the order
+of its entries, so use a `LinkedHashMap`). The other message headers are not 
used, and when the header is not set, the
+exchange fails.
+
+._Java-only: inline Processor that sets the List of the values_
+[source,java]
+----
+from("direct:projects")
+    .process(exchange -> 
exchange.getIn().setHeader(JdbcConstants.JDBC_PARAMETERS, List.of("ASF", 123)))
+    .setBody(constant("select * from projects where license = ? and id > ? 
order by id"))
+    .to("jdbc:myDataSource?useHeadersAsParameters=true");
+----
+
 == Examples
 
 In the following example, we set up the DataSource that camel-jdbc requires.
diff --git 
a/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json
 
b/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json
index e05623176303..1693491f416d 100644
--- 
a/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json
+++ 
b/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json
@@ -33,14 +33,14 @@
     "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is an UPDATE, query the update count is 
returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" },
     "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is a SELECT and the outputType is 
SelectList, the row count is returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" },
     "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Set<String>", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The column names from the ResultSet as a 
java.util.Set type.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" },
-    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "Map", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "A java.util.Map which has the headers to be used if 
useHeadersAsParameters has been enabled.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" },
+    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Object", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The values of the query parameters when 
useHeadersAsParameters has been enabled: a java.util.Map with the values of the 
named parameters, or a java.util.List with the values of the positional 
placeholders, in order.", "constantName":  [...]
     "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "false", "description": "Set its value 
to true to retrieve generated keys", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" },
     "CamelGeneratedColumns": { "index": 5, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"String[] or int[]", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Set it to specify the expected 
generated columns", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_COLUMNS" },
     "CamelGeneratedKeysRowCount": { "index": 6, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The number of rows in the header that 
contains generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_ROW_COUNT" },
     "CamelGeneratedKeysRows": { "index": 7, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"List<Map<String, Object>>", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Rows that contains the 
generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_DATA" }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its placeholders come from the 
CamelJdbcParameters header, or else from the headers" },
+    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its named parameters (:?name) come from 
the CamelJdbcParameters header, or else from the headers, and the values of 
plain ? placeholders, in order, only from the CamelJdbcParameters header" },
     "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "outputType", "value": "SelectOne", "carriers": [ "map", "text", 
"value", "bytes", "pojo", "null", "unchanged" ], "javaTypeOption": 
"outputClass" }, { "option": "outputType", "value": "StreamList", "carriers": [ 
"iterator", "unchanged" ] } ], "description": "The rows of a select: a List of 
Maps of the column names and their values (beans of outputClass when set). 
SelectOne gives the Map of the one row, th [...]
   },
   "properties": {
@@ -53,7 +53,7 @@
     "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": 
"Reset Auto Commit", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "description": "Camel will set 
the autoCommit on the JDBC connection to be false, commit the change after 
executed the statement and reset the autoCommit flag of the connection at the 
end, if the resetAutoCommit is true. If [...]
     "transacted": { "index": 7, "kind": "parameter", "displayName": 
"Transacted", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not 
used: the producer does not read this option", "autowired": false, "secret": 
false, "defaultValue": false, "description": "Whether transactions are in use. 
Deprecation note: Not used: the producer does not read this option" },
     "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": 
"Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "To read BLOB 
columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read 
after the producer has closed the connection. This may be needed for certain 
databases such as Oracle where yo [...]
-    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
+    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
     "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", 
"displayName": "Use JDBC4 Column Name And Label Semantics", "group": 
"producer", "label": "", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or 
older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get 
the column name where as JDBC 3.0 uses both [...]
     "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
     "beanRowMapper": { "index": 12, "kind": "parameter", "displayName": "Bean 
Row Mapper", "group": "advanced", "label": "advanced", "required": false, 
"type": "object", "javaType": "org.apache.camel.component.jdbc.BeanRowMapper", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
use a custom org.apache.camel.component.jdbc.BeanRowMapper when using 
outputClass. The default implementation will lower case the row names and skip 
underscores, and dashes. For exampl [...]
diff --git a/components/camel-jdbc/src/main/docs/jdbc-component.adoc 
b/components/camel-jdbc/src/main/docs/jdbc-component.adoc
index c055e8f3c8b2..5c691ac96fd9 100644
--- a/components/camel-jdbc/src/main/docs/jdbc-component.adoc
+++ b/components/camel-jdbc/src/main/docs/jdbc-component.adoc
@@ -159,6 +159,22 @@ YAML::
 You can also store the header values in a `java.util.Map` and store the
 map on the headers with the key `CamelJdbcParameters`.
 
+=== Using plain ? placeholders
+
+A query with plain `?` placeholders (and no named parameters) takes the values 
of the placeholders, in order, only from
+the `CamelJdbcParameters` header, as a `java.util.List` (a `java.util.Map` 
works too, with its values taken in the order
+of its entries, so use a `LinkedHashMap`). The other message headers are not 
used, and when the header is not set, the
+exchange fails.
+
+._Java-only: inline Processor that sets the List of the values_
+[source,java]
+----
+from("direct:projects")
+    .process(exchange -> 
exchange.getIn().setHeader(JdbcConstants.JDBC_PARAMETERS, List.of("ASF", 123)))
+    .setBody(constant("select * from projects where license = ? and id > ? 
order by id"))
+    .to("jdbc:myDataSource?useHeadersAsParameters=true");
+----
+
 == Examples
 
 In the following example, we set up the DataSource that camel-jdbc requires.
diff --git 
a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/DefaultJdbcPrepareStatementStrategy.java
 
b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/DefaultJdbcPrepareStatementStrategy.java
index e494c44fbbb5..613a1eceadef 100644
--- 
a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/DefaultJdbcPrepareStatementStrategy.java
+++ 
b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/DefaultJdbcPrepareStatementStrategy.java
@@ -18,6 +18,7 @@ package org.apache.camel.component.jdbc;
 
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -55,19 +56,19 @@ public class DefaultJdbcPrepareStatementStrategy implements 
JdbcPrepareStatement
             final String query, final String preparedQuery, final int 
expectedParams,
             final Exchange exchange, final Object value)
             throws SQLException {
-        Map<?, ?> map = null;
-        if (exchange.getIn().hasHeaders()) {
-            if (exchange.getIn().getHeader(JdbcConstants.JDBC_PARAMETERS) != 
null) {
-                // header JDBC_PARAMETERS takes precedence over regular headers
-                map = 
exchange.getIn().getHeader(JdbcConstants.JDBC_PARAMETERS, Map.class);
-            } else {
-                map = exchange.getIn().getHeaders();
+        if (hasNamedParameters(query)) {
+            Map<?, ?> map = null;
+            if (exchange.getIn().hasHeaders()) {
+                if (exchange.getIn().getHeader(JdbcConstants.JDBC_PARAMETERS) 
!= null) {
+                    // header JDBC_PARAMETERS takes precedence over regular 
headers
+                    map = 
exchange.getIn().getHeader(JdbcConstants.JDBC_PARAMETERS, Map.class);
+                } else {
+                    map = exchange.getIn().getHeaders();
+                }
             }
-        }
 
-        final Map<?, ?> headerMap = map;
+            final Map<?, ?> headerMap = map;
 
-        if (hasNamedParameters(query)) {
             // create an iterator that returns the value in the named order
             try {
 
@@ -118,9 +119,32 @@ public class DefaultJdbcPrepareStatementStrategy 
implements JdbcPrepareStatement
             }
 
         } else {
-            // just use a regular iterator
-            return 
exchange.getContext().getTypeConverter().convertTo(Iterator.class,
-                    headerMap != null ? headerMap.values() : null);
+            // plain ? placeholders are bound by position, so the values are 
only taken from the JDBC_PARAMETERS header
+            // (and not from all the message headers, as their order does not 
relate to the query)
+            return createPositionalIterator(query, exchange);
+        }
+    }
+
+    private Iterator<?> createPositionalIterator(String query, Exchange 
exchange) {
+        Object parameters = 
exchange.getIn().getHeader(JdbcConstants.JDBC_PARAMETERS);
+        if (parameters instanceof Map<?, ?> map) {
+            return map.values().iterator();
+        } else if (parameters instanceof Iterable<?> iterable) {
+            return iterable.iterator();
+        } else if (parameters instanceof Object[] array) {
+            return Arrays.asList(array).iterator();
+        } else if (parameters == null) {
+            throw new RuntimeExchangeException(
+                    "Cannot set the ? placeholders in query [" + query + "]: 
set their values, in order, as a List in the "
+                                               + JdbcConstants.JDBC_PARAMETERS
+                                               + " header, or use named 
parameters (:?name)",
+                    exchange);
+        } else {
+            throw new RuntimeExchangeException(
+                    "Cannot set the ? placeholders in query [" + query + "]: 
the " + JdbcConstants.JDBC_PARAMETERS
+                                               + " header must be a List or a 
Map with their values, in order, but was "
+                                               + 
parameters.getClass().getName(),
+                    exchange);
         }
     }
 
diff --git 
a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java
 
b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java
index 786c9f1c7606..a672e020af7a 100644
--- 
a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java
+++ 
b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java
@@ -35,9 +35,11 @@ public final class JdbcConstants {
                                                 "type.",
               javaType = "Set<String>")
     public static final String JDBC_COLUMN_NAMES = "CamelJdbcColumnNames";
-    @Metadata(label = "producer", description = "A `java.util.Map` which has 
the headers to be used if\n" +
-                                                "`useHeadersAsParameters` has 
been enabled.",
-              javaType = "Map")
+    @Metadata(label = "producer",
+              description = "The values of the query parameters when 
`useHeadersAsParameters` has been enabled: a\n" +
+                            "`java.util.Map` with the values of the named 
parameters, or a `java.util.List` with the\n" +
+                            "values of the positional placeholders, in order.",
+              javaType = "Object")
     public static final String JDBC_PARAMETERS = "CamelJdbcParameters";
 
     /**
diff --git 
a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java
 
b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java
index 26545f4053d2..d5a146c7986a 100644
--- 
a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java
+++ 
b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java
@@ -42,8 +42,9 @@ import org.apache.camel.support.DefaultEndpoint;
 @BodyTypes(producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, 
Carrier.STREAM, Carrier.FILE },
                                   description = "The SQL statement as text, or 
bytes, a stream or a file of it; a Map or a List"
                                                 + " is not a statement: set 
the SQL as the body. With useHeadersAsParameters,"
-                                                + " the values of its 
placeholders come from the CamelJdbcParameters header, or"
-                                                + " else from the headers"),
+                                                + " the values of its named 
parameters (:?name) come from the"
+                                                + " CamelJdbcParameters 
header, or else from the headers, and the values of"
+                                                + " plain ? placeholders, in 
order, only from the CamelJdbcParameters header"),
            producerOut = @BodyType(value = { Carrier.LIST, Carrier.UNCHANGED },
                                    byOption = {
                                            @BodyType.When(option = 
"outputType", value = "SelectOne",
@@ -243,7 +244,9 @@ public class JdbcEndpoint extends DefaultEndpoint {
 
     /**
      * Set this option to true to use the prepareStatementStrategy with named 
parameters. This allows to define queries
-     * with named placeholders, and use headers with the dynamic values for 
the query placeholders.
+     * with named placeholders, and use headers with the dynamic values for 
the query placeholders. A query with only
+     * positional placeholders (no named parameters) takes their values, in 
order, only from the CamelJdbcParameters
+     * header (a List).
      */
     public void setUseHeadersAsParameters(boolean useHeadersAsParameters) {
         this.useHeadersAsParameters = useHeadersAsParameters;
diff --git 
a/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcParameterizedQueryTest.java
 
b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcParameterizedQueryTest.java
index e573f022392a..3614ea3c0685 100644
--- 
a/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcParameterizedQueryTest.java
+++ 
b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcParameterizedQueryTest.java
@@ -17,15 +17,20 @@
 package org.apache.camel.component.jdbc;
 
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.RuntimeExchangeException;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.camel.test.junit6.TestSupport.assertIsInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class JdbcParameterizedQueryTest extends AbstractJdbcTestSupport {
 
@@ -34,13 +39,14 @@ public class JdbcParameterizedQueryTest extends 
AbstractJdbcTestSupport {
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
 
-        // The linkedHashMap values has different order in JDK7 and JDK8
-        // so I had to reduce the parameters size
-        Map<String, Object> jdbcParams = new HashMap<>();
-        jdbcParams.put("name", "jstrachan");
+        Map<String, Object> headers = new LinkedHashMap<>();
+        // other headers are not bound to the ? placeholders
+        headers.put("foo", "cust2");
+        headers.put("bar", "nsandhu");
+        headers.put(JdbcConstants.JDBC_PARAMETERS, List.of("cust1", 
"jstrachan"));
 
-        template.sendBodyAndHeaders("direct:start", "select * from customer 
where id = 'cust1' and name = ? order by ID",
-                jdbcParams);
+        template.sendBodyAndHeaders("direct:start", "select * from customer 
where id = ? and name = ? order by ID",
+                headers);
 
         MockEndpoint.assertIsSatisfied(context);
 
@@ -50,6 +56,53 @@ public class JdbcParameterizedQueryTest extends 
AbstractJdbcTestSupport {
         assertEquals("jstrachan", row.get("NAME"));
     }
 
+    @Test
+    public void testParameterizedQueryNoNamesMap() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(1);
+
+        // the values of a map are bound in the order of its entries
+        Map<String, Object> jdbcParams = new LinkedHashMap<>();
+        jdbcParams.put("id", "cust3");
+        jdbcParams.put("name", "willem");
+
+        Map<String, Object> headers = new LinkedHashMap<>();
+        headers.put("foo", "cust2");
+        headers.put(JdbcConstants.JDBC_PARAMETERS, jdbcParams);
+
+        template.sendBodyAndHeaders("direct:start", "select * from customer 
where id = ? and name = ? order by ID",
+                headers);
+
+        MockEndpoint.assertIsSatisfied(context);
+
+        List<?> received = assertIsInstanceOf(List.class, 
mock.getReceivedExchanges().get(0).getIn().getBody());
+        assertEquals(1, received.size());
+        Map<?, ?> row = assertIsInstanceOf(Map.class, received.get(0));
+        assertEquals("willem", row.get("NAME"));
+    }
+
+    @Test
+    public void testParameterizedQueryNoNamesNotFromHeaders() {
+        Map<String, Object> headers = new LinkedHashMap<>();
+        headers.put("id", "cust1");
+        headers.put("name", "jstrachan");
+
+        CamelExecutionException e = assertThrows(CamelExecutionException.class,
+                () -> template.sendBodyAndHeaders("direct:start",
+                        "select * from customer where id = ? and name = ? 
order by ID", headers));
+        RuntimeExchangeException cause = 
assertIsInstanceOf(RuntimeExchangeException.class, e.getCause());
+        assertTrue(cause.getMessage().contains("as a List in the 
CamelJdbcParameters header"), cause.getMessage());
+    }
+
+    @Test
+    public void testParameterizedQueryNoNamesInvalidParameters() {
+        CamelExecutionException e = assertThrows(CamelExecutionException.class,
+                () -> template.sendBodyAndHeader("direct:start",
+                        "select * from customer where id = ? order by ID", 
JdbcConstants.JDBC_PARAMETERS, "cust1"));
+        RuntimeExchangeException cause = 
assertIsInstanceOf(RuntimeExchangeException.class, e.getCause());
+        assertTrue(cause.getMessage().contains("must be a List or a Map"), 
cause.getMessage());
+    }
+
     @Test
     public void testParameterizedQuery() throws Exception {
         MockEndpoint mock = getMockEndpoint("mock:result");
diff --git 
a/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json
 
b/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json
index 905518eb3d89..e1c237003a65 100644
--- 
a/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json
+++ 
b/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json
@@ -33,14 +33,14 @@
     "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is an UPDATE, query the update count is 
returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" },
     "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "If the query is a SELECT and the outputType is 
SelectList, the row count is returned in this OUT header.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" },
     "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Set<String>", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The column names from the ResultSet as a 
java.util.Set type.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" },
-    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": "Map", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "A java.util.Map which has the headers to be used if 
useHeadersAsParameters has been enabled.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" },
+    "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "producer", "required": false, "javaType": 
"Object", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The values of the query parameters when 
useHeadersAsParameters has been enabled: a java.util.Map with the values of the 
named parameters, or a java.util.List with the values of the positional 
placeholders, in order.", "constantName":  [...]
     "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "false", "description": "Set its value 
to true to retrieve generated keys", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" },
     "CamelGeneratedColumns": { "index": 5, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"String[] or int[]", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Set it to specify the expected 
generated columns", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_COLUMNS" },
     "CamelGeneratedKeysRowCount": { "index": 6, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The number of rows in the header that 
contains generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_ROW_COUNT" },
     "CamelGeneratedKeysRows": { "index": 7, "kind": "header", "displayName": 
"", "group": "producer", "label": "producer", "required": false, "javaType": 
"List<Map<String, Object>>", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Rows that contains the 
generated keys.", "constantName": 
"org.apache.camel.component.jdbc.JdbcConstants#JDBC_GENERATED_KEYS_DATA" }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its placeholders come from the 
CamelJdbcParameters header, or else from the headers" },
+    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"description": "The SQL statement as text, or bytes, a stream or a file of it; 
a Map or a List is not a statement: set the SQL as the body. With 
useHeadersAsParameters, the values of its named parameters (:?name) come from 
the CamelJdbcParameters header, or else from the headers, and the values of 
plain ? placeholders, in order, only from the CamelJdbcParameters header" },
     "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ { 
"option": "outputType", "value": "SelectOne", "carriers": [ "map", "text", 
"value", "bytes", "pojo", "null", "unchanged" ], "javaTypeOption": 
"outputClass" }, { "option": "outputType", "value": "StreamList", "carriers": [ 
"iterator", "unchanged" ] } ], "description": "The rows of a select: a List of 
Maps of the column names and their values (beans of outputClass when set). 
SelectOne gives the Map of the one row, th [...]
   },
   "properties": {
@@ -53,7 +53,7 @@
     "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": 
"Reset Auto Commit", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "description": "Camel will set 
the autoCommit on the JDBC connection to be false, commit the change after 
executed the statement and reset the autoCommit flag of the connection at the 
end, if the resetAutoCommit is true. If [...]
     "transacted": { "index": 7, "kind": "parameter", "displayName": 
"Transacted", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not 
used: the producer does not read this option", "autowired": false, "secret": 
false, "defaultValue": false, "description": "Deprecation note: Not used: the 
producer does not read this option" },
     "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": 
"Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "To read BLOB 
columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read 
after the producer has closed the connection. This may be needed for certain 
databases such as Oracle where yo [...]
-    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
+    "useHeadersAsParameters": { "index": 9, "kind": "parameter", 
"displayName": "Use Headers As Parameters", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "Set this option to true to use the prepareStatementStrategy 
with named parameters. This allows to define queries with named placeholders, 
and use headers with the dynamic values for the query [...]
     "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", 
"displayName": "Use JDBC4 Column Name And Label Semantics", "group": 
"producer", "label": "", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or 
older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get 
the column name where as JDBC 3.0 uses both [...]
     "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
     "beanRowMapper": { "index": 12, "kind": "parameter", "displayName": "Bean 
Row Mapper", "group": "advanced", "label": "advanced", "required": false, 
"type": "object", "javaType": "org.apache.camel.component.jdbc.BeanRowMapper", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
use a custom org.apache.camel.component.jdbc.BeanRowMapper when using 
outputClass. The default implementation will lower case the row names and skip 
underscores, and dashes. For exampl [...]
diff --git 
a/components/camel-spring-parent/camel-spring-jdbc/src/main/java/org/apache/camel/component/spring/jdbc/SpringJdbcEndpoint.java
 
b/components/camel-spring-parent/camel-spring-jdbc/src/main/java/org/apache/camel/component/spring/jdbc/SpringJdbcEndpoint.java
index be34e4e8860c..14228b9ff76e 100644
--- 
a/components/camel-spring-parent/camel-spring-jdbc/src/main/java/org/apache/camel/component/spring/jdbc/SpringJdbcEndpoint.java
+++ 
b/components/camel-spring-parent/camel-spring-jdbc/src/main/java/org/apache/camel/component/spring/jdbc/SpringJdbcEndpoint.java
@@ -35,8 +35,9 @@ import org.apache.camel.spi.UriEndpoint;
 @BodyTypes(producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, 
Carrier.STREAM, Carrier.FILE },
                                   description = "The SQL statement as text, or 
bytes, a stream or a file of it; a Map or a List"
                                                 + " is not a statement: set 
the SQL as the body. With useHeadersAsParameters,"
-                                                + " the values of its 
placeholders come from the CamelJdbcParameters header, or"
-                                                + " else from the headers"),
+                                                + " the values of its named 
parameters (:?name) come from the"
+                                                + " CamelJdbcParameters 
header, or else from the headers, and the values of"
+                                                + " plain ? placeholders, in 
order, only from the CamelJdbcParameters header"),
            producerOut = @BodyType(value = { Carrier.LIST, Carrier.UNCHANGED },
                                    byOption = {
                                            @BodyType.When(option = 
"outputType", value = "SelectOne",
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index c7fa7e136c87..ab5af7824254 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1190,6 +1190,19 @@ CamelContext of that provider. It now closes each of 
them only when no other `JC
 it returned the closed cache, whose operations throw `IllegalStateException`. 
This matters for code that calls a
 `JCacheManager` (for example one created by a custom `JCacheManagerFactory`) 
after it was closed.
 
+=== camel-jdbc, camel-spring-jdbc - plain ? placeholders take their values 
only from the CamelJdbcParameters header
+
+With `useHeadersAsParameters=true` and a query with only plain `?` 
placeholders (no named parameters `:?name`), the
+`jdbc` and `spring-jdbc` producers bound the values of all the message headers 
by position, or of the
+`CamelJdbcParameters` header when it was set. Which value went to which `?` 
then depended on the headers of the
+message and their order (which changed in 4.21 from alphabetical to the order 
they were added). The values of plain
+`?` placeholders now come only from the `CamelJdbcParameters` header: a `List` 
with the values in the order of the
+placeholders (or a `Map`, whose values are taken in the order of its entries). 
When the header is not set, the
+exchange fails with a `RuntimeExchangeException`. Set the values in that 
header, or use named parameters.
+
+Named parameters work as before: their values come from the 
`CamelJdbcParameters` header (a `Map`) when it is set, or
+else from the message headers.
+
 === camel-jgroups-raft - a body that does not convert to a byte array fails 
the exchange
 
 The `jgroups-raft` producer skipped a message body that has no conversion to 
`byte[]`, such as a `Map`, a POJO or a
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java
index d70b960c88c7..d7513bf8576a 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java
@@ -310,7 +310,9 @@ public interface JdbcEndpointBuilderFactory {
          * Set this option to true to use the prepareStatementStrategy with
          * named parameters. This allows to define queries with named
          * placeholders, and use headers with the dynamic values for the query
-         * placeholders.
+         * placeholders. A query with only positional placeholders (no named
+         * parameters) takes their values, in order, only from the
+         * CamelJdbcParameters header (a List).
          * 
          * The option is a: <code>boolean</code> type.
          * 
@@ -328,7 +330,9 @@ public interface JdbcEndpointBuilderFactory {
          * Set this option to true to use the prepareStatementStrategy with
          * named parameters. This allows to define queries with named
          * placeholders, and use headers with the dynamic values for the query
-         * placeholders.
+         * placeholders. A query with only positional placeholders (no named
+         * parameters) takes their values, in order, only from the
+         * CamelJdbcParameters header (a List).
          * 
          * The option will be converted to a <code>boolean</code> type.
          * 
@@ -659,10 +663,12 @@ public interface JdbcEndpointBuilderFactory {
             return "CamelJdbcColumnNames";
         }
         /**
-         * A java.util.Map which has the headers to be used if
-         * useHeadersAsParameters has been enabled.
+         * The values of the query parameters when useHeadersAsParameters has
+         * been enabled: a java.util.Map with the values of the named
+         * parameters, or a java.util.List with the values of the positional
+         * placeholders, in order.
          * 
-         * The option is a: {@code Map} type.
+         * The option is a: {@code Object} type.
          * 
          * Group: producer
          * 
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java
index 8a05397e4f0c..0c49324312b0 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java
@@ -304,7 +304,9 @@ public interface SpringJdbcEndpointBuilderFactory {
          * Set this option to true to use the prepareStatementStrategy with
          * named parameters. This allows to define queries with named
          * placeholders, and use headers with the dynamic values for the query
-         * placeholders.
+         * placeholders. A query with only positional placeholders (no named
+         * parameters) takes their values, in order, only from the
+         * CamelJdbcParameters header (a List).
          * 
          * The option is a: <code>boolean</code> type.
          * 
@@ -322,7 +324,9 @@ public interface SpringJdbcEndpointBuilderFactory {
          * Set this option to true to use the prepareStatementStrategy with
          * named parameters. This allows to define queries with named
          * placeholders, and use headers with the dynamic values for the query
-         * placeholders.
+         * placeholders. A query with only positional placeholders (no named
+         * parameters) takes their values, in order, only from the
+         * CamelJdbcParameters header (a List).
          * 
          * The option will be converted to a <code>boolean</code> type.
          * 
@@ -656,10 +660,12 @@ public interface SpringJdbcEndpointBuilderFactory {
             return "CamelJdbcColumnNames";
         }
         /**
-         * A java.util.Map which has the headers to be used if
-         * useHeadersAsParameters has been enabled.
+         * The values of the query parameters when useHeadersAsParameters has
+         * been enabled: a java.util.Map with the values of the named
+         * parameters, or a java.util.List with the values of the positional
+         * placeholders, in order.
          * 
-         * The option is a: {@code Map} type.
+         * The option is a: {@code Object} type.
          * 
          * Group: producer
          * 

Reply via email to