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

CritasWang pushed a commit to branch wx/iotdb-2.0.11-alignment
in repository https://gitbox.apache.org/repos/asf/iotdb-extras.git

commit 339d6baa55a18c68cce48e1e49e49f932e9c7047
Author: CritasWang <[email protected]>
AuthorDate: Thu Sep 17 17:50:47 2026 +0800

    mybatis: drop unsupported UPDATE statements and ship the runtime jar
    
    IoTDB 2.0.11 UPDATE rejects time in its predicate and FIELD columns in
    SET, so MBG's key-based UPDATE statements can never run. IoTDBKeyPlugin
    now disables them with a generator warning instead of emitting SQL that
    fails at runtime, and gains unit coverage for the nullable-TAG predicate
    rewrite it already performed.
    
    The mybatis-support runtime jar is required by every generated mapper
    but was only reachable through mvn install; the new with-mybatis
    distribution profile packages it next to the generator plugin.
---
 README-zh.md                                       |   6 +
 README.md                                          |   6 +
 distributions/pom.xml                              |  70 ++++++++
 .../src/assembly/mybatis-generator-plugin.xml      |   8 +
 mybatis-generator/README-zh.md                     |   4 +-
 mybatis-generator/README.md                        |   4 +-
 .../iotdb/mybatis/plugin/IoTDBKeyPlugin.java       |  28 +++-
 .../iotdb/mybatis/plugin/IoTDBKeyPluginTest.java   | 180 +++++++++++++++++++++
 mybatis-support/README.md                          |   2 +
 9 files changed, 305 insertions(+), 3 deletions(-)

diff --git a/README-zh.md b/README-zh.md
index da442a5..3616075 100644
--- a/README-zh.md
+++ b/README-zh.md
@@ -142,6 +142,12 @@ IoTDB-Extras 使用 Maven profiles 配置不同的构建选项。您可以组合
   mvn clean package -Pwith-flink -DskipTests
   ```
 
+- **with-mybatis**:将 MyBatis 生成插件和运行时适配打包为分发 zip
+
+  ```bash
+  mvn clean package -Pwith-mybatis -DskipTests
+  ```
+
 - **with-grafana**:构建 Grafana 连接器和插件
 
   ```bash
diff --git a/README.md b/README.md
index ee42dd1..3cc7831 100644
--- a/README.md
+++ b/README.md
@@ -143,6 +143,12 @@ IoTDB-Extras uses Maven profiles to configure different 
build options. You can c
   mvn clean package -Pwith-flink -DskipTests
   ```
 
+- **with-mybatis**: Package the MyBatis generator plugin and runtime support 
as a distribution zip
+
+  ```bash
+  mvn clean package -Pwith-mybatis -DskipTests
+  ```
+
 - **with-grafana**: Build Grafana connectors and plugins
 
   ```bash
diff --git a/distributions/pom.xml b/distributions/pom.xml
index cc002aa..ae4f4f0 100644
--- a/distributions/pom.xml
+++ b/distributions/pom.xml
@@ -119,6 +119,76 @@
                 </plugins>
             </build>
         </profile>
+        <profile>
+            <id>with-mybatis</id>
+            <dependencies>
+                <!-- Reactor ordering only: the assembly picks the jars up 
from the module target dirs. -->
+                <dependency>
+                    <groupId>org.apache.iotdb</groupId>
+                    <artifactId>mybatis-generator-plugin</artifactId>
+                    <version>${project.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.iotdb</groupId>
+                    <artifactId>mybatis-support</artifactId>
+                    <version>${project.version}</version>
+                </dependency>
+            </dependencies>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <executions>
+                            <!-- Package binaries-->
+                            <execution>
+                                <id>all-bin</id>
+                                <goals>
+                                    <goal>single</goal>
+                                </goals>
+                                <phase>package</phase>
+                                <configuration>
+                                    <descriptors>
+                                        
<descriptor>src/assembly/mybatis-generator-plugin.xml</descriptor>
+                                    </descriptors>
+                                    
<finalName>apache-iotdb-${project.version}</finalName>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <!--
+                      Create SHA512 checksum files for the release artifacts.
+                    -->
+                    <plugin>
+                        <groupId>net.nicoulaj.maven.plugins</groupId>
+                        <artifactId>checksum-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>sign-source-release</id>
+                                <goals>
+                                    <goal>files</goal>
+                                </goals>
+                                <phase>package</phase>
+                                <configuration>
+                                    <algorithms>
+                                        <algorithm>SHA-512</algorithm>
+                                    </algorithms>
+                                    <fileSets>
+                                        <!--bin-all-->
+                                        <fileSet>
+                                            
<directory>${project.build.directory}</directory>
+                                            <includes>
+                                                
<include>apache-iotdb-${project.version}-mybatis-generator-plugin-bin.zip</include>
+                                            </includes>
+                                        </fileSet>
+                                    </fileSets>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
         <profile>
             <id>with-flink</id>
             <build>
diff --git a/distributions/src/assembly/mybatis-generator-plugin.xml 
b/distributions/src/assembly/mybatis-generator-plugin.xml
index 271c084..dc00b42 100644
--- a/distributions/src/assembly/mybatis-generator-plugin.xml
+++ b/distributions/src/assembly/mybatis-generator-plugin.xml
@@ -34,6 +34,14 @@
                 <include>mybatis-generator-plugin-*.jar</include>
             </includes>
         </fileSet>
+        <!-- The generated mappers need the runtime adapters (query 
interceptor, DATE/BLOB handlers). -->
+        <fileSet>
+            
<directory>${maven.multiModuleProjectDirectory}/mybatis-support/target/</directory>
+            <outputDirectory>${file.separator}</outputDirectory>
+            <includes>
+                <include>mybatis-support-*.jar</include>
+            </includes>
+        </fileSet>
     </fileSets>
     <componentDescriptors>
         <componentDescriptor>common-files.xml</componentDescriptor>
diff --git a/mybatis-generator/README-zh.md b/mybatis-generator/README-zh.md
index c18b5f6..bee354a 100644
--- a/mybatis-generator/README-zh.md
+++ b/mybatis-generator/README-zh.md
@@ -31,6 +31,8 @@
 mvn -pl mybatis-generator,mybatis-support -am clean install
 ```
 
+`mvn package -Pwith-mybatis` 还会生成 
`distributions/target/apache-iotdb-<version>-mybatis-generator-plugin-bin.zip`,其中同时打包生成插件和
 `mybatis-support` 运行时 jar。
+
 生成时,`mybatis-generator-maven-plugin:1.4.2` 的 `dependencies` 中必须包含:
 
 | 依赖 | 版本 |
@@ -61,7 +63,7 @@ mvn mybatis-generator:generate
 - 行键由 **TIME 与全部 TAG** 组成,`virtualKeyColumns` 必须与表结构一致。`IoTDBKeyPlugin` 
在查询、删除的键谓词中为 NULL TAG 生成 `IS NULL`。
 - `IoTDBJavaTypeResolver` 将 TIMESTAMP 映射为 **Long**;数值单位跟随服务端 
ms/us/ns,适配层不转换单位。FLOAT 配置为 `java.lang.Float`。不要用 `Date` 承接高精度时间戳。
 - DATE 使用 `LocalDate + IoTDBLocalDateTypeHandler`,BLOB 使用 `byte[] + 
IoTDBBlobTypeHandler`。通过 `columnOverride` 
同时生成参数及结果映射,详见[运行时适配文档](../mybatis-support/README.md)。
-- 设置 `enableUpdateByPrimaryKey=false` 和 `enableUpdateByExample=false`。2.0.11 的 
UPDATE 只支持 ATTRIBUTE;修改 FIELD 要按相同行键执行 INSERT。省略或为 NULL 的 FIELD 不会清除已有值。
+- 设置 `enableUpdateByPrimaryKey=false` 和 `enableUpdateByExample=false`。2.0.11 的 
UPDATE 只支持 ATTRIBUTE,且谓词中不能出现 `time`,MBG 按主键生成的 UPDATE 
无法执行;若仍开启,`IoTDBKeyPlugin` 会跳过这些语句并输出生成警告。修改 FIELD 要按相同行键执行 INSERT。省略或为 NULL 的 
FIELD 不会清除已有值。
 - ATTRIBUTE 属于设备,更新会影响该设备所有时间点。普通关系数据库的通用 UPDATE 不能直接套用。
 - 批量 SQL 保留 MBG 的标识符转义和 TypeHandler。保留字使用 `delimitIdentifiers` / 
`delimitAllColumns`。
 - 示例使用 `ignoreQualifiersAtRuntime=true`:生成读取配置中的 schema,运行时由 JDBC URL 选择数据库。
diff --git a/mybatis-generator/README.md b/mybatis-generator/README.md
index 1750e78..713164b 100644
--- a/mybatis-generator/README.md
+++ b/mybatis-generator/README.md
@@ -33,6 +33,8 @@ From the repository root:
 mvn -pl mybatis-generator,mybatis-support -am clean install
 ```
 
+`mvn package -Pwith-mybatis` additionally builds 
`distributions/target/apache-iotdb-<version>-mybatis-generator-plugin-bin.zip`, 
which bundles the generator plugin jar together with the `mybatis-support` 
runtime jar.
+
 ## Configure the generator
 
 Both the plugin and JDBC driver must be in the generator's plugin classloader. 
No absolute `classPathEntry` or manually copied JDBC jar is needed:
@@ -86,7 +88,7 @@ Applications also need 
[mybatis-support](../mybatis-support/README.md), includin
 
 - `IoTDBJavaTypeResolver` maps TIMESTAMP to **Long**. Values use the server's 
configured ms/us/ns precision without conversion. Set 
`jdbcType.FLOAT=java.lang.Float`; do not map high-precision timestamps to 
`Date`.
 - The logical row identity is **TIME plus every TAG**. Keep 
`virtualKeyColumns` synchronized with the real schema; `IoTDBKeyPlugin` emits 
`IS NULL` for nullable TAG components in generated SELECT/DELETE predicates. 
ATTRIBUTE and FIELD columns are not keys.
-- Disable `enableUpdateByPrimaryKey` and `enableUpdateByExample`. IoTDB 2.0.11 
UPDATE changes ATTRIBUTE columns only. To change FIELD values, INSERT the same 
key and the desired fields; omitted/null fields do not erase existing values. 
ATTRIBUTE updates affect the device across timestamps.
+- Disable `enableUpdateByPrimaryKey` and `enableUpdateByExample`. IoTDB 2.0.11 
UPDATE changes ATTRIBUTE columns only and rejects `time` in its predicate, so 
MBG's key-based UPDATE statements cannot run; `IoTDBKeyPlugin` drops them and 
reports a generator warning if they are left enabled. To change FIELD values, 
INSERT the same key and the desired fields; omitted/null fields do not erase 
existing values. ATTRIBUTE updates affect the device across timestamps.
 - Add DATE/BLOB `columnOverride` entries from [runtime 
support](../mybatis-support/README.md), so the same handlers apply to inserts, 
batch parameters and result maps.
 - Use `delimitIdentifiers` / `delimitAllColumns` for SQL identifiers requiring 
quotes. Batch SQL uses MBG's formatting helpers and preserves configured 
handlers and escaping.
 - Lombok/Swagger plugins require the corresponding annotation dependencies in 
the consuming application. Lombok is applied to primary-key and BLOB model 
classes as well as base records.
diff --git 
a/mybatis-generator/src/main/java/org/apache/iotdb/mybatis/plugin/IoTDBKeyPlugin.java
 
b/mybatis-generator/src/main/java/org/apache/iotdb/mybatis/plugin/IoTDBKeyPlugin.java
index 1681ba1..2a1a0d6 100644
--- 
a/mybatis-generator/src/main/java/org/apache/iotdb/mybatis/plugin/IoTDBKeyPlugin.java
+++ 
b/mybatis-generator/src/main/java/org/apache/iotdb/mybatis/plugin/IoTDBKeyPlugin.java
@@ -24,16 +24,42 @@ import org.mybatis.generator.api.dom.xml.Attribute;
 import org.mybatis.generator.api.dom.xml.TextElement;
 import org.mybatis.generator.api.dom.xml.XmlElement;
 import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
+import org.mybatis.generator.config.TableConfiguration;
 
+import java.util.ArrayList;
 import java.util.List;
 
-/** Preserves nullable TAG components in generated TIME + TAG key predicates. 
*/
+/**
+ * Adapts MBG's key-based statements to the IoTDB table model: nullable TAG 
components of the TIME +
+ * TAG key become {@code IS NULL} predicates in SELECT/DELETE, and the UPDATE 
statements are dropped
+ * because IoTDB UPDATE accepts neither {@code time} in the predicate nor 
FIELD columns in SET.
+ */
 public class IoTDBKeyPlugin extends PluginAdapter {
+  // MBG hands the shared warnings list to validate() before any table 
callback.
+  private List<String> warnings = new ArrayList<>();
+
   @Override
   public boolean validate(List<String> warnings) {
+    this.warnings = warnings;
     return true;
   }
 
+  @Override
+  public void initialized(IntrospectedTable table) {
+    TableConfiguration configuration = table.getTableConfiguration();
+    if (configuration.isUpdateByPrimaryKeyStatementEnabled()
+        || configuration.isUpdateByExampleStatementEnabled()) {
+      warnings.add(
+          "IoTDBKeyPlugin: not generating UPDATE statements for "
+              + table.getFullyQualifiedTable()
+              + "; IoTDB UPDATE cannot use time in the predicate or FIELD 
columns in SET. INSERT"
+              + " the same key to change FIELD values and set 
enableUpdateByPrimaryKey and"
+              + " enableUpdateByExample to false.");
+      configuration.setUpdateByPrimaryKeyStatementEnabled(false);
+      configuration.setUpdateByExampleStatementEnabled(false);
+    }
+  }
+
   @Override
   public boolean sqlMapSelectByPrimaryKeyElementGenerated(
       XmlElement element, IntrospectedTable table) {
diff --git 
a/mybatis-generator/src/test/java/org/apache/iotdb/mybatis/plugin/IoTDBKeyPluginTest.java
 
b/mybatis-generator/src/test/java/org/apache/iotdb/mybatis/plugin/IoTDBKeyPluginTest.java
new file mode 100644
index 0000000..22356bf
--- /dev/null
+++ 
b/mybatis-generator/src/test/java/org/apache/iotdb/mybatis/plugin/IoTDBKeyPluginTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.iotdb.mybatis.plugin;
+
+import org.apache.ibatis.builder.xml.XMLMapperBuilder;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.session.Configuration;
+import org.junit.Test;
+import org.mybatis.generator.api.IntrospectedColumn;
+import org.mybatis.generator.api.IntrospectedTable;
+import org.mybatis.generator.api.dom.DefaultXmlFormatter;
+import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
+import org.mybatis.generator.api.dom.xml.Attribute;
+import org.mybatis.generator.api.dom.xml.Document;
+import org.mybatis.generator.api.dom.xml.TextElement;
+import org.mybatis.generator.api.dom.xml.XmlElement;
+import org.mybatis.generator.codegen.mybatis3.IntrospectedTableMyBatis3Impl;
+import org.mybatis.generator.config.Context;
+import org.mybatis.generator.config.ModelType;
+import org.mybatis.generator.config.TableConfiguration;
+import org.mybatis.generator.internal.rules.FlatModelRules;
+
+import java.io.StringReader;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class IoTDBKeyPluginTest {
+  private final Context context = new Context(ModelType.FLAT);
+  private final List<String> warnings = new ArrayList<>();
+  private final IoTDBKeyPlugin plugin = new IoTDBKeyPlugin();
+
+  private IntrospectedTable table() {
+    context.addProperty("beginningDelimiter", "\"");
+    context.addProperty("endingDelimiter", "\"");
+    plugin.setContext(context);
+    assertTrue(plugin.validate(warnings));
+    IntrospectedTable table =
+        new IntrospectedTableMyBatis3Impl() {
+          @Override
+          public String getFullyQualifiedTableNameAtRuntime() {
+            return "mix";
+          }
+        };
+    TableConfiguration configuration = new TableConfiguration(context);
+    configuration.setTableName("mix");
+    table.setTableConfiguration(configuration);
+    table.setContext(context);
+    table.setBaseRecordType("example.Mix");
+    table.setRules(new FlatModelRules(table));
+    column(table, "time", Types.TIMESTAMP, "TIMESTAMP", "java.lang.Long");
+    column(table, "device_id", Types.VARCHAR, "VARCHAR", "java.lang.String");
+    column(table, "temperature", Types.FLOAT, "FLOAT", "java.lang.Float");
+    table.addPrimaryKeyColumn("time");
+    table.addPrimaryKeyColumn("device_id");
+    return table;
+  }
+
+  private void column(
+      IntrospectedTable table, String name, int type, String jdbc, String 
javaType) {
+    IntrospectedColumn column = new IntrospectedColumn();
+    column.setContext(context);
+    column.setActualColumnName(name);
+    column.setJavaProperty(
+        name.equals("device_id") ? "deviceId" : name); // MBG camel-cases the 
property name
+    column.setColumnNameDelimited(true); // matches delimitAllColumns in the 
example configs
+    column.setJdbcType(type);
+    column.setJdbcTypeName(jdbc);
+    column.setFullyQualifiedJavaType(new FullyQualifiedJavaType(javaType));
+    table.addColumn(column);
+  }
+
+  /** Mirrors the trailing "where ... and ..." text elements MBG emits for the 
key predicate. */
+  private XmlElement keyedStatement(String tag, String id, String prefix) {
+    XmlElement element = new XmlElement(tag);
+    element.addAttribute(new Attribute("id", id));
+    if (tag.equals("select")) {
+      element.addAttribute(new Attribute("resultType", "map"));
+    }
+    element.addElement(new TextElement(prefix + " \"mix\""));
+    element.addElement(new TextElement("where \"time\" = 
#{time,jdbcType=TIMESTAMP}"));
+    element.addElement(new TextElement("  and \"device_id\" = 
#{deviceId,jdbcType=VARCHAR}"));
+    return element;
+  }
+
+  private Configuration parse(XmlElement... statements) {
+    Document document =
+        new Document(
+            "-//mybatis.org//DTD Mapper 3.0//EN", 
"https://mybatis.org/dtd/mybatis-3-mapper.dtd";);
+    XmlElement root = new XmlElement("mapper");
+    root.addAttribute(new Attribute("namespace", "example.MixMapper"));
+    for (XmlElement statement : statements) {
+      root.addElement(statement);
+    }
+    document.setRootElement(root);
+    Configuration configuration = new Configuration();
+    new XMLMapperBuilder(
+            new StringReader(new 
DefaultXmlFormatter().getFormattedContent(document)),
+            configuration,
+            "mapper.xml",
+            configuration.getSqlFragments())
+        .parse();
+    return configuration;
+  }
+
+  @Test
+  public void rewritesKeyPredicatesSoNullTagsMatchWithIsNull() {
+    IntrospectedTable table = table();
+    XmlElement select = keyedStatement("select", "selectByPrimaryKey", "select 
* from");
+    XmlElement delete = keyedStatement("delete", "deleteByPrimaryKey", "delete 
from");
+    assertTrue(plugin.sqlMapSelectByPrimaryKeyElementGenerated(select, table));
+    assertTrue(plugin.sqlMapDeleteByPrimaryKeyElementGenerated(delete, table));
+    Configuration configuration = parse(select, delete);
+    for (String statement : new String[] {"selectByPrimaryKey", 
"deleteByPrimaryKey"}) {
+      Map<String, Object> key = new HashMap<>();
+      key.put("time", 1700000000123L);
+      key.put("deviceId", "d1");
+      BoundSql bound =
+          configuration.getMappedStatement("example.MixMapper." + 
statement).getBoundSql(key);
+      String sql = bound.getSql().replaceAll("\\s+", " ");
+      assertTrue(sql, sql.contains("WHERE \"time\" = ? AND \"device_id\" = 
?"));
+      assertEquals(2, bound.getParameterMappings().size());
+      key.put("deviceId", null);
+      bound = configuration.getMappedStatement("example.MixMapper." + 
statement).getBoundSql(key);
+      sql = bound.getSql().replaceAll("\\s+", " ");
+      assertTrue(sql, sql.contains("WHERE \"time\" = ? AND \"device_id\" IS 
NULL"));
+      assertEquals(1, bound.getParameterMappings().size());
+    }
+  }
+
+  @Test
+  public void leavesStatementsWithoutAKeyPredicateAlone() {
+    IntrospectedTable table = table();
+    XmlElement select = new XmlElement("select");
+    select.addAttribute(new Attribute("id", "selectAll"));
+    select.addElement(new TextElement("select * from \"mix\""));
+    assertTrue(plugin.sqlMapSelectByPrimaryKeyElementGenerated(select, table));
+    assertEquals(1, select.getElements().size());
+  }
+
+  @Test
+  public void disablesUpdateStatementsWithAWarning() {
+    IntrospectedTable table = table();
+    TableConfiguration configuration = table.getTableConfiguration();
+    configuration.setUpdateByPrimaryKeyStatementEnabled(true);
+    configuration.setUpdateByExampleStatementEnabled(true);
+    plugin.initialized(table);
+    assertFalse(configuration.isUpdateByPrimaryKeyStatementEnabled());
+    assertFalse(configuration.isUpdateByExampleStatementEnabled());
+    assertFalse(table.getRules().generateUpdateByPrimaryKeyWithoutBLOBs());
+    assertFalse(table.getRules().generateUpdateByPrimaryKeySelective());
+    assertFalse(table.getRules().generateUpdateByExampleWithoutBLOBs());
+    assertEquals(1, warnings.size());
+    assertTrue(warnings.get(0), warnings.get(0).contains("not generating 
UPDATE statements"));
+
+    plugin.initialized(table);
+    assertEquals("already disabled tables do not warn again", 1, 
warnings.size());
+  }
+}
diff --git a/mybatis-support/README.md b/mybatis-support/README.md
index f4d68ac..ef7c576 100644
--- a/mybatis-support/README.md
+++ b/mybatis-support/README.md
@@ -31,6 +31,8 @@ From the repository root:
 mvn -pl mybatis-support -am install
 ```
 
+The `with-mybatis` distribution profile (`mvn package -Pwith-mybatis`) also 
ships this jar next to the generator plugin in 
`apache-iotdb-<version>-mybatis-generator-plugin-bin.zip`.
+
 Add this application dependency (it is separate from the build-time generator 
plugin):
 
 ```xml

Reply via email to