This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch backport/29084-to-camel-4.22.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit f714ebf8274dee40041319d60b326086999b1b93 Author: Guillaume Nodet <[email protected]> AuthorDate: Tue Nov 3 12:03:38 2026 +0000 [backport camel-4.22.x] CAMEL-26533: camel-jpa - bind CamelJpaParameters header values literally --- .../org/apache/camel/catalog/components/jpa.json | 2 +- .../META-INF/org/apache/camel/component/jpa/jpa.json | 2 +- .../org/apache/camel/component/jpa/JpaConstants.java | 3 ++- .../java/org/apache/camel/component/jpa/JpaProducer.java | 13 ++++++------- .../jpa/JpaProducerWithQueryParametersHeaderTest.java | 16 +++++++++++++--- .../modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 10 ++++++++++ .../builder/endpoint/dsl/JpaEndpointBuilderFactory.java | 1 + 7 files changed, 34 insertions(+), 13 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jpa.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jpa.json index 93a85f144ca0..e1f729d0b72f 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jpa.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jpa.json @@ -37,7 +37,7 @@ }, "headers": { "CamelEntityManager": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "jakarta.persistence.EntityManager", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The JPA EntityManager object.", "constantName": "org.apache.camel.component.jpa.JpaConstants#ENTITY_MANAGER" }, - "CamelJpaParameters": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Alternative way for passing query parameters as an Exchange header.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_PARAMETERS_HEADER" }, + "CamelJpaParameters": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Alternative way for passing query parameters as an Exchange header. The parameter values are used as-is without expression evaluation.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_PARAMETERS_HEADER" }, "CamelJpaMaximumResults": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Defines the maximum number of results to retrieve on the query; takes precedence over the value set on the endpoint, if any.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_MAXIMUM_RESULTS" }, "CamelJpaFirstResult": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Defines the position of the first result to retrieve; takes precedence over the value set on the endpoint, if any.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_FIRST_RESULT" } }, diff --git a/components/camel-jpa/src/generated/resources/META-INF/org/apache/camel/component/jpa/jpa.json b/components/camel-jpa/src/generated/resources/META-INF/org/apache/camel/component/jpa/jpa.json index 93a85f144ca0..e1f729d0b72f 100644 --- a/components/camel-jpa/src/generated/resources/META-INF/org/apache/camel/component/jpa/jpa.json +++ b/components/camel-jpa/src/generated/resources/META-INF/org/apache/camel/component/jpa/jpa.json @@ -37,7 +37,7 @@ }, "headers": { "CamelEntityManager": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "jakarta.persistence.EntityManager", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The JPA EntityManager object.", "constantName": "org.apache.camel.component.jpa.JpaConstants#ENTITY_MANAGER" }, - "CamelJpaParameters": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Alternative way for passing query parameters as an Exchange header.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_PARAMETERS_HEADER" }, + "CamelJpaParameters": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Alternative way for passing query parameters as an Exchange header. The parameter values are used as-is without expression evaluation.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_PARAMETERS_HEADER" }, "CamelJpaMaximumResults": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Defines the maximum number of results to retrieve on the query; takes precedence over the value set on the endpoint, if any.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_MAXIMUM_RESULTS" }, "CamelJpaFirstResult": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Defines the position of the first result to retrieve; takes precedence over the value set on the endpoint, if any.", "constantName": "org.apache.camel.component.jpa.JpaConstants#JPA_FIRST_RESULT" } }, diff --git a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaConstants.java b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaConstants.java index 4273b1394f46..9cc7cbf04fa6 100644 --- a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaConstants.java +++ b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaConstants.java @@ -26,7 +26,8 @@ public final class JpaConstants { @Metadata(description = "The JPA `EntityManager` object.", javaType = "jakarta.persistence.EntityManager") public static final String ENTITY_MANAGER = Exchange.JPA_ENTITY_MANAGER; - @Metadata(label = "producer", description = "Alternative way for passing query parameters as an Exchange header.", + @Metadata(label = "producer", + description = "Alternative way for passing query parameters as an Exchange header. The parameter values are used as-is without expression evaluation.", javaType = "Map<String, Object>") public static final String JPA_PARAMETERS_HEADER = "CamelJpaParameters"; @Metadata(label = "producer", description = "Defines the maximum number of results to retrieve on the query; " + diff --git a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java index b0b0ac831aa3..81065da0724b 100644 --- a/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java +++ b/components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaProducer.java @@ -201,20 +201,19 @@ public class JpaProducer extends DefaultProducer { query.setFirstResult(firstResult); } // setup the parameters - Map<String, ?> params; if (parameters != null) { - params = parameters; - } else { - params = exchange.getIn().getHeader(JpaConstants.JPA_PARAMETERS_HEADER, Map.class); - } - if (params != null) { - params.forEach((key, value) -> { + parameters.forEach((key, value) -> { Object resolvedValue = value; if (value instanceof String str) { resolvedValue = simple.createExpression(str).evaluate(exchange, Object.class); } query.setParameter(key, resolvedValue); }); + } else { + Map<String, ?> params = exchange.getIn().getHeader(JpaConstants.JPA_PARAMETERS_HEADER, Map.class); + if (params != null) { + params.forEach(query::setParameter); + } } } diff --git a/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaProducerWithQueryParametersHeaderTest.java b/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaProducerWithQueryParametersHeaderTest.java index 4ee30bfb0be3..1ff35a4c9fa4 100644 --- a/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaProducerWithQueryParametersHeaderTest.java +++ b/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaProducerWithQueryParametersHeaderTest.java @@ -46,17 +46,27 @@ public class JpaProducerWithQueryParametersHeaderTest { Customer c2 = new Customer(); c2.setName("Dummy"); template.sendBody("direct:addCustomer", c2); + Customer c3 = new Customer(); + c3.setName("${body}"); + template.sendBody("direct:addCustomer", c3); Map<String, Object> params = new HashMap<>(); - params.put("custName", "${body}"); + params.put("custName", "Willem"); - List list = template.requestBodyAndHeader("direct:namedQuery", "Willem", JpaConstants.JPA_PARAMETERS_HEADER, params, + List list = template.requestBodyAndHeader("direct:namedQuery", "foo", JpaConstants.JPA_PARAMETERS_HEADER, params, List.class); assertEquals(1, list.size()); assertEquals("Willem", ((Customer) list.get(0)).getName()); + // String parameter values in header must be treated literally and not evaluated as Simple expressions + params.put("custName", "${body}"); + list = template.requestBodyAndHeader("direct:namedQuery", "Willem", JpaConstants.JPA_PARAMETERS_HEADER, params, + List.class); + assertEquals(1, list.size()); + assertEquals("${body}", ((Customer) list.get(0)).getName()); + int integer = template.requestBody("direct:deleteCustomers", null, int.class); - assertEquals(2, integer); + assertEquals(3, integer); } @BeforeEach diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc index 75842677ea21..61d16611ad0f 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc @@ -11,6 +11,16 @@ Note that manual migration is still required. See the xref:camel-upgrade-recipes-tool.adoc[documentation] page for details. ==== +== Upgrading from 4.22.3 to 4.22.4 + +=== camel-jpa - query parameters from CamelJpaParameters header are bound literally + +In previous versions, string values supplied in the `CamelJpaParameters` header map were evaluated as Simple expressions by the JPA producer. + +Starting in 4.22.4, parameter values provided via the `CamelJpaParameters` header are used as-is and bound literally as query parameters without expression evaluation. The endpoint URI option `parameters` continues to support Simple expression evaluation for route-configured parameters. + +Routes that previously relied on evaluating Simple expressions within the `CamelJpaParameters` header should evaluate those expressions in the route before setting the header value. + == Upgrading from 4.22.2 to 4.22.3 === camel-as2 - server/listen - signature and MIC over the message as received diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JpaEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JpaEndpointBuilderFactory.java index 84096d374ac2..a057578ec82b 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JpaEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JpaEndpointBuilderFactory.java @@ -2273,6 +2273,7 @@ public interface JpaEndpointBuilderFactory { } /** * Alternative way for passing query parameters as an Exchange header. + * The parameter values are used as-is without expression evaluation. * * The option is a: {@code Map<String, Object>} type. *
