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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 97460eb125c3 CAMEL-25508: camel-mybatis - Use a String or byte[] body 
as one parameter for Insert, Update and Delete
97460eb125c3 is described below

commit 97460eb125c36effe44495332d88065930c3f173
Author: allthingssecurity <[email protected]>
AuthorDate: Sat Oct 10 13:06:23 2026 +0530

    CAMEL-25508: camel-mybatis - Use a String or byte[] body as one parameter 
for Insert, Update and Delete
    
    MyBatisProducer ran Insert, Update and Delete statements once per element
    of ObjectHelper.createIterator(body), with only a Map passed as-is.
    createIterator also splits a String at every comma and iterates a byte[]
    byte by byte, so inserting "Doe, Jr." inserted "Doe" and " Jr.", and a
    byte[] ran the statement once per byte.
    
    A Map, a String (also a blank one, which did not run the statement
    before) and a byte[] are now passed as one parameter; MyBatis wraps an
    array parameter into a parameter map, so the statement refers to the
    byte[] as #{array}. Collections, iterators, streams and all other arrays,
    including int[] and long[], still run the statement once per element.
    This is a breaking change for a route that relied on the String being
    split, as described in the 4.23 upgrade guide.
    
    Closes #27641
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../camel/component/mybatis/MyBatisProducer.java   | 37 ++++-----
 .../mybatis/MyBatisStringParameterTest.java        | 89 ++++++++++++++++++++++
 .../org/apache/camel/component/mybatis/Account.xml | 24 ++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  9 +++
 4 files changed, 138 insertions(+), 21 deletions(-)

diff --git 
a/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisProducer.java
 
b/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisProducer.java
index 4e54d2689a26..be3320002846 100644
--- 
a/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisProducer.java
+++ 
b/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisProducer.java
@@ -127,13 +127,7 @@ public class MyBatisProducer extends DefaultProducer {
         Object result;
         Object in = getInput(exchange);
         if (in != null) {
-            Iterator<?> iter;
-            if (in instanceof Map) {
-                // we want the map as-is
-                iter = Collections.singletonList(in).iterator();
-            } else {
-                iter = ObjectHelper.createIterator(in);
-            }
+            Iterator<?> iter = createIterator(in);
             while (iter.hasNext()) {
                 Object value = iter.next();
                 LOG.trace("Inserting: {} using statement: {}", value, 
statement);
@@ -166,13 +160,7 @@ public class MyBatisProducer extends DefaultProducer {
         Object result;
         Object in = getInput(exchange);
         if (in != null) {
-            Iterator<?> iter;
-            if (in instanceof Map) {
-                // we want the map as-is
-                iter = Collections.singletonList(in).iterator();
-            } else {
-                iter = ObjectHelper.createIterator(in);
-            }
+            Iterator<?> iter = createIterator(in);
             while (iter.hasNext()) {
                 Object value = iter.next();
                 LOG.trace("Updating: {} using statement: {}", value, 
statement);
@@ -205,13 +193,7 @@ public class MyBatisProducer extends DefaultProducer {
         Object result;
         Object in = getInput(exchange);
         if (in != null) {
-            Iterator<?> iter;
-            if (in instanceof Map) {
-                // we want the map as-is
-                iter = Collections.singletonList(in).iterator();
-            } else {
-                iter = ObjectHelper.createIterator(in);
-            }
+            Iterator<?> iter = createIterator(in);
             while (iter.hasNext()) {
                 Object value = iter.next();
                 LOG.trace("Deleting: {} using statement: {}", value, 
statement);
@@ -297,6 +279,19 @@ public class MyBatisProducer extends DefaultProducer {
         return (MyBatisEndpoint) super.getEndpoint();
     }
 
+    /**
+     * Iterates the input to run the statement once per element: a collection, 
an iterator, a stream or an array
+     * (including an array of a primitive type such as a long[] of ids). A 
Map, a String (which may contain commas) and
+     * a byte[] are one parameter as-is; MyBatis wraps an array parameter into 
a map, so a statement refers to a byte[]
+     * as #{array}.
+     */
+    private static Iterator<?> createIterator(Object in) {
+        if (in instanceof Map || in instanceof String || in instanceof byte[]) 
{
+            return Collections.singletonList(in).iterator();
+        }
+        return ObjectHelper.createIterator(in);
+    }
+
     private Object getInput(final Exchange exchange) {
         final String inputHeader = getEndpoint().getInputHeader();
         if (inputHeader != null) {
diff --git 
a/components/camel-mybatis/src/test/java/org/apache/camel/component/mybatis/MyBatisStringParameterTest.java
 
b/components/camel-mybatis/src/test/java/org/apache/camel/component/mybatis/MyBatisStringParameterTest.java
new file mode 100644
index 000000000000..dfa34f482284
--- /dev/null
+++ 
b/components/camel-mybatis/src/test/java/org/apache/camel/component/mybatis/MyBatisStringParameterTest.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.mybatis;
+
+import java.nio.charset.StandardCharsets;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A String or byte[] body is one parameter, also when the String contains a 
comma or is blank; collections, iterators
+ * and other arrays run the statement once per element.
+ */
+public class MyBatisStringParameterTest extends MyBatisTestSupport {
+
+    @Test
+    public void testInsertStringWithComma() {
+        
template.sendBody("mybatis:insertAccountWithLastName?statementType=Insert", 
"Doe, Jr.");
+
+        assertEquals(3, rowCount(), "There should be 3 rows");
+        Account account = 
template.requestBody("mybatis:selectAccountById?statementType=SelectOne", 789, 
Account.class);
+        assertEquals("Doe, Jr.", account.getLastName());
+    }
+
+    @Test
+    public void testUpdateStringWithComma() {
+        
template.sendBody("mybatis:updateLastNameOfAccount123?statementType=Update", 
"Strachan, Jr.");
+
+        Account account = 
template.requestBody("mybatis:selectAccountById?statementType=SelectOne", 123, 
Account.class);
+        assertEquals("Strachan, Jr.", account.getLastName());
+    }
+
+    @Test
+    public void testDeleteStringWithComma() {
+        
template.sendBody("mybatis:updateLastNameOfAccount123?statementType=Update", 
"Ibsen, Strachan");
+
+        // deletes account 123 only, not the account 456 with the last name 
Ibsen
+        
template.sendBody("mybatis:deleteAccountByLastName?statementType=Delete", 
"Ibsen, Strachan");
+
+        assertEquals(1, rowCount(), "There should be 1 row");
+        Account account = 
template.requestBody("mybatis:selectAccountById?statementType=SelectOne", 456, 
Account.class);
+        assertEquals("Ibsen", account.getLastName());
+    }
+
+    @Test
+    public void testInsertBlankString() {
+        
template.sendBody("mybatis:insertAccountWithLastName?statementType=Insert", "");
+
+        assertEquals(3, rowCount(), "There should be 3 rows");
+        Account account = 
template.requestBody("mybatis:selectAccountById?statementType=SelectOne", 789, 
Account.class);
+        assertEquals("", account.getLastName());
+    }
+
+    @Test
+    public void testInsertByteArray() {
+        
template.sendBody("mybatis:insertAccountWithLastNameBytes?statementType=Insert",
+                "Doe".getBytes(StandardCharsets.UTF_8));
+
+        assertEquals(3, rowCount(), "There should be 3 rows");
+        Account account = 
template.requestBody("mybatis:selectAccountById?statementType=SelectOne", 789, 
Account.class);
+        assertEquals("Doe", account.getLastName());
+    }
+
+    @Test
+    public void testDeleteLongArrayRunsOncePerElement() {
+        
template.sendBody("mybatis:deleteAccountByLongId?statementType=Delete", new 
long[] { 123L, 456L });
+
+        assertEquals(0, rowCount(), "There should be 0 rows");
+    }
+
+    private int rowCount() {
+        return template.requestBody("mybatis:count?statementType=SelectOne", 
null, Integer.class);
+    }
+}
diff --git 
a/components/camel-mybatis/src/test/resources/org/apache/camel/component/mybatis/Account.xml
 
b/components/camel-mybatis/src/test/resources/org/apache/camel/component/mybatis/Account.xml
index 207064d951cc..789547a47fe3 100644
--- 
a/components/camel-mybatis/src/test/resources/org/apache/camel/component/mybatis/Account.xml
+++ 
b/components/camel-mybatis/src/test/resources/org/apache/camel/component/mybatis/Account.xml
@@ -115,6 +115,30 @@ aliases to match the properties of the target result 
class. -->
         </foreach>
     </delete>
 
+    <!-- Insert, update and delete examples, using a String as the parameter 
class -->
+    <insert id="insertAccountWithLastName" parameterType="String">
+        insert into ACCOUNT (ACC_ID, ACC_FIRST_NAME, ACC_LAST_NAME, ACC_EMAIL)
+        values (789, 'Jane', #{lastName}, '[email protected]')
+    </insert>
+
+    <update id="updateLastNameOfAccount123" parameterType="String">
+        update ACCOUNT set ACC_LAST_NAME = #{lastName} where ACC_ID = 123
+    </update>
+
+    <delete id="deleteAccountByLastName" parameterType="String">
+        delete from ACCOUNT where ACC_LAST_NAME = #{lastName}
+    </delete>
+
+    <!-- A byte[] is one parameter, which MyBatis wraps into a map with the 
key array -->
+    <insert id="insertAccountWithLastNameBytes">
+        insert into ACCOUNT (ACC_ID, ACC_FIRST_NAME, ACC_LAST_NAME, ACC_EMAIL)
+        values (789, 'Jane', UTF8TOSTRING(#{array}), '[email protected]')
+    </insert>
+
+    <delete id="deleteAccountByLongId" parameterType="long">
+        delete from ACCOUNT where ACC_ID = #{id}
+    </delete>
+
     <!-- START SNIPPET: e1 -->
     <select id="selectUnprocessedAccounts" resultMap="AccountResult">
         select * from ACCOUNT where PROCESSED = false
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 5a2073eb349b..7b50fb025307 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -854,6 +854,15 @@ and most services, which were not registered at this level 
already). Prior to Ca
 every component. Use `RoutesOnly` (or `Default`) to keep the component MBeans. 
The CamelContext, health check and route
 controller MBeans are still registered.
 
+=== camel-mybatis - a String or byte[] body is one parameter (Breaking change)
+
+With `statementType=Insert`, `Update` or `Delete`, a `String` body (or input 
header) is now passed to the statement as
+one parameter, also when it contains commas or is blank, and so is a `byte[]`. 
MyBatis wraps an array parameter into a
+parameter map, so the statement refers to the `byte[]` as `+#{array}+`. Prior 
to Camel 4.23 the String was split at each
+comma and the statement ran once for each part (once for each byte of a 
`byte[]`, and not at all for a blank String).
+Collections, iterators and other arrays (including `int[]`, `long[]` and other 
primitive arrays) still run the statement
+once per element; send a `List` to run it once per value.
+
 === camel-groovy
 
 A `GroovyShellFactory` is now looked up in the registry once per 
`CamelContext`, when the first groovy expression is

Reply via email to