This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch camel-4.4.x in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.4.x by this push: new 84ecb1faddb CAMEL-21112: camel-sql - SQL producer in batch mode using inlined simple expression should use the individual batch body when evaluating. Thanks to Alexander Zobkov for the analysis. (#15315) 84ecb1faddb is described below commit 84ecb1faddb9ebd927269f547c9b2cbe6db9949b Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Aug 26 15:36:33 2024 +0200 CAMEL-21112: camel-sql - SQL producer in batch mode using inlined simple expression should use the individual batch body when evaluating. Thanks to Alexander Zobkov for the analysis. (#15315) --- .../sql/DefaultSqlPrepareStatementStrategy.java | 16 +++- .../component/sql/ProducerBatchSimpleExpTest.java | 89 ++++++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/components/camel-sql/src/main/java/org/apache/camel/component/sql/DefaultSqlPrepareStatementStrategy.java b/components/camel-sql/src/main/java/org/apache/camel/component/sql/DefaultSqlPrepareStatementStrategy.java index 143ad54e3b4..e3f5eab4c2f 100644 --- a/components/camel-sql/src/main/java/org/apache/camel/component/sql/DefaultSqlPrepareStatementStrategy.java +++ b/components/camel-sql/src/main/java/org/apache/camel/component/sql/DefaultSqlPrepareStatementStrategy.java @@ -29,7 +29,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.camel.Exchange; +import org.apache.camel.Expression; import org.apache.camel.RuntimeExchangeException; +import org.apache.camel.support.ExchangeHelper; import org.apache.camel.support.ObjectHelper; import org.apache.camel.util.StringQuoteHelper; import org.slf4j.Logger; @@ -69,7 +71,7 @@ public class DefaultSqlPrepareStatementStrategy implements SqlPrepareStatementSt Matcher matcher = REPLACE_IN_PATTERN.matcher(query); while (matcher.find()) { String found = matcher.group(1); - Object parameter = lookupParameter(found, exchange, exchange.getIn().getBody()); + Object parameter = lookupParameter(found, exchange, null); if (parameter != null) { Iterator<?> it = createInParameterIterator(parameter); StringJoiner replaceBuilder = new StringJoiner(","); @@ -259,14 +261,20 @@ public class DefaultSqlPrepareStatementStrategy implements SqlPrepareStatementSt } } - protected static Object lookupParameter(String nextParam, Exchange exchange, Object body) { + protected static Object lookupParameter(String nextParam, Exchange exchange, Object batchBody) { + Object body = batchBody != null ? batchBody : exchange.getMessage().getBody(); Map<?, ?> bodyMap = safeMap(exchange.getContext().getTypeConverter().tryConvertTo(Map.class, body)); Map<?, ?> headersMap = safeMap(exchange.getIn().getHeaders()); Object answer = null; if ((nextParam.startsWith("$simple{") || nextParam.startsWith("${")) && nextParam.endsWith("}")) { - answer = exchange.getContext().resolveLanguage("simple").createExpression(nextParam).evaluate(exchange, - Object.class); + if (batchBody != null) { + // in batch mode then need to work on a copy of the original exchange and set the batch body + exchange = ExchangeHelper.createCopy(exchange, true); + exchange.getMessage().setBody(batchBody); + } + Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(nextParam); + answer = exp.evaluate(exchange, Object.class); } else if (bodyMap.containsKey(nextParam)) { answer = bodyMap.get(nextParam); } else if (headersMap.containsKey(nextParam)) { diff --git a/components/camel-sql/src/test/java/org/apache/camel/component/sql/ProducerBatchSimpleExpTest.java b/components/camel-sql/src/test/java/org/apache/camel/component/sql/ProducerBatchSimpleExpTest.java new file mode 100644 index 00000000000..0735971fd3e --- /dev/null +++ b/components/camel-sql/src/test/java/org/apache/camel/component/sql/ProducerBatchSimpleExpTest.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.sql; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.junit.jupiter.api.Test; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ProducerBatchSimpleExpTest extends CamelTestSupport { + + private EmbeddedDatabase db; + private JdbcTemplate jdbcTemplate; + + public record MyData(int id, String project, String license) { + } + + @Override + public void doPreSetup() throws Exception { + db = new EmbeddedDatabaseBuilder() + .setName(getClass().getSimpleName()) + .setType(EmbeddedDatabaseType.H2) + .addScript("sql/createAndPopulateDatabase.sql").build(); + + jdbcTemplate = new JdbcTemplate(db); + } + + @Override + public void doPostTearDown() throws Exception { + if (db != null) { + db.shutdown(); + } + } + + @Test + public void shouldExecuteBatch() throws InterruptedException { + MockEndpoint mock = getMockEndpoint("mock:query"); + mock.expectedMessageCount(1); + + List data = new ArrayList(); + data.add(new MyData(4, "Donald", "DIS")); + data.add(new MyData(5, "Goofy", "DIS")); + template.requestBody("direct:query", data); + + MockEndpoint.assertIsSatisfied(context); + + assertEquals("Donald", jdbcTemplate.queryForObject("select project from projects where id = 4", String.class)); + assertEquals("Goofy", jdbcTemplate.queryForObject("select project from projects where id = 5", String.class)); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + getContext().getComponent("sql", SqlComponent.class).setDataSource(db); + + from("direct:query") + .to("sql:insert into projects values " + + "(:#$simple{body.id},:#$simple{body.project},:#$simple{body.license})" + + "?batch=true") + .to("mock:query"); + } + }; + } +}